xref: /aosp_15_r20/external/pdfium/fpdfsdk/fpdf_view.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "public/fpdfview.h"
8 
9 #include <memory>
10 #include <utility>
11 #include <vector>
12 
13 #include "build/build_config.h"
14 #include "core/fpdfapi/page/cpdf_docpagedata.h"
15 #include "core/fpdfapi/page/cpdf_occontext.h"
16 #include "core/fpdfapi/page/cpdf_page.h"
17 #include "core/fpdfapi/page/cpdf_pageimagecache.h"
18 #include "core/fpdfapi/page/cpdf_pagemodule.h"
19 #include "core/fpdfapi/parser/cpdf_array.h"
20 #include "core/fpdfapi/parser/cpdf_dictionary.h"
21 #include "core/fpdfapi/parser/cpdf_document.h"
22 #include "core/fpdfapi/parser/cpdf_name.h"
23 #include "core/fpdfapi/parser/cpdf_parser.h"
24 #include "core/fpdfapi/parser/cpdf_stream.h"
25 #include "core/fpdfapi/parser/cpdf_string.h"
26 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
27 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
28 #include "core/fpdfapi/render/cpdf_pagerendercontext.h"
29 #include "core/fpdfapi/render/cpdf_rendercontext.h"
30 #include "core/fpdfapi/render/cpdf_renderoptions.h"
31 #include "core/fpdfdoc/cpdf_nametree.h"
32 #include "core/fpdfdoc/cpdf_viewerpreferences.h"
33 #include "core/fxcrt/cfx_read_only_span_stream.h"
34 #include "core/fxcrt/fx_safe_types.h"
35 #include "core/fxcrt/fx_stream.h"
36 #include "core/fxcrt/fx_system.h"
37 #include "core/fxcrt/span_util.h"
38 #include "core/fxcrt/stl_util.h"
39 #include "core/fxcrt/unowned_ptr.h"
40 #include "core/fxge/cfx_defaultrenderdevice.h"
41 #include "core/fxge/cfx_gemodule.h"
42 #include "core/fxge/cfx_renderdevice.h"
43 #include "core/fxge/dib/cfx_dibitmap.h"
44 #include "fpdfsdk/cpdfsdk_customaccess.h"
45 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
46 #include "fpdfsdk/cpdfsdk_helpers.h"
47 #include "fpdfsdk/cpdfsdk_pageview.h"
48 #include "fpdfsdk/cpdfsdk_renderpage.h"
49 #include "fxjs/ijs_runtime.h"
50 #include "public/fpdf_formfill.h"
51 #include "third_party/base/check_op.h"
52 #include "third_party/base/containers/span.h"
53 #include "third_party/base/memory/ptr_util.h"
54 #include "third_party/base/numerics/safe_conversions.h"
55 
56 #ifdef PDF_ENABLE_V8
57 #include "fxjs/cfx_v8_array_buffer_allocator.h"
58 #include "third_party/base/no_destructor.h"
59 #endif
60 
61 #ifdef PDF_ENABLE_XFA
62 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
63 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
64 #endif  // PDF_ENABLE_XFA
65 
66 #if BUILDFLAG(IS_WIN)
67 #include "core/fpdfapi/render/cpdf_progressiverenderer.h"
68 #include "core/fpdfapi/render/cpdf_windowsrenderdevice.h"
69 #include "public/fpdf_edit.h"
70 
71 #if defined(_SKIA_SUPPORT_)
72 class SkCanvas;
73 #endif  // defined(_SKIA_SUPPORT_)
74 
75 // These checks are here because core/ and public/ cannot depend on each other.
76 static_assert(static_cast<int>(WindowsPrintMode::kEmf) == FPDF_PRINTMODE_EMF,
77               "WindowsPrintMode::kEmf value mismatch");
78 static_assert(static_cast<int>(WindowsPrintMode::kTextOnly) ==
79                   FPDF_PRINTMODE_TEXTONLY,
80               "WindowsPrintMode::kTextOnly value mismatch");
81 static_assert(static_cast<int>(WindowsPrintMode::kPostScript2) ==
82                   FPDF_PRINTMODE_POSTSCRIPT2,
83               "WindowsPrintMode::kPostScript2 value mismatch");
84 static_assert(static_cast<int>(WindowsPrintMode::kPostScript3) ==
85                   FPDF_PRINTMODE_POSTSCRIPT3,
86               "WindowsPrintMode::kPostScript3 value mismatch");
87 static_assert(static_cast<int>(WindowsPrintMode::kPostScript2PassThrough) ==
88                   FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH,
89               "WindowsPrintMode::kPostScript2PassThrough value mismatch");
90 static_assert(static_cast<int>(WindowsPrintMode::kPostScript3PassThrough) ==
91                   FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH,
92               "WindowsPrintMode::kPostScript3PassThrough value mismatch");
93 static_assert(static_cast<int>(WindowsPrintMode::kEmfImageMasks) ==
94                   FPDF_PRINTMODE_EMF_IMAGE_MASKS,
95               "WindowsPrintMode::kEmfImageMasks value mismatch");
96 static_assert(static_cast<int>(WindowsPrintMode::kPostScript3Type42) ==
97                   FPDF_PRINTMODE_POSTSCRIPT3_TYPE42,
98               "WindowsPrintMode::kPostScript3Type42 value mismatch");
99 static_assert(
100     static_cast<int>(WindowsPrintMode::kPostScript3Type42PassThrough) ==
101         FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH,
102     "WindowsPrintMode::kPostScript3Type42PassThrough value mismatch");
103 #endif  // BUILDFLAG(IS_WIN)
104 
105 #if defined(_SKIA_SUPPORT_)
106 // These checks are here because core/ and public/ cannot depend on each other.
107 static_assert(static_cast<int>(CFX_DefaultRenderDevice::RendererType::kAgg) ==
108                   FPDF_RENDERERTYPE_AGG,
109               "CFX_DefaultRenderDevice::RendererType::kAGG value mismatch");
110 static_assert(static_cast<int>(CFX_DefaultRenderDevice::RendererType::kSkia) ==
111                   FPDF_RENDERERTYPE_SKIA,
112               "CFX_DefaultRenderDevice::RendererType::kSkia value mismatch");
113 #endif  // defined(_SKIA_SUPPORT_)
114 
115 namespace {
116 
117 bool g_bLibraryInitialized = false;
118 
UseRendererType(FPDF_RENDERER_TYPE public_type)119 void UseRendererType(FPDF_RENDERER_TYPE public_type) {
120   // Internal definition of renderer types must stay updated with respect to
121   // the public definition, such that all public definitions can be mapped to
122   // an internal definition in `CFX_DefaultRenderDevice`. A public definition
123   // value might not be meaningful for a particular build configuration, which
124   // would mean use of that value is an error for that build.
125 
126   // AGG is always present in a build. |FPDF_RENDERERTYPE_SKIA| is valid to use
127   // only if it is included in the build.
128 #if defined(_SKIA_SUPPORT_)
129   // This build configuration has the option for runtime renderer selection.
130   if (public_type == FPDF_RENDERERTYPE_AGG ||
131       public_type == FPDF_RENDERERTYPE_SKIA) {
132     CFX_DefaultRenderDevice::SetDefaultRenderer(
133         static_cast<CFX_DefaultRenderDevice::RendererType>(public_type));
134     return;
135   }
136   CHECK(false);
137 #else
138   // `FPDF_RENDERERTYPE_AGG` is used for fully AGG builds.
139   CHECK_EQ(public_type, FPDF_RENDERERTYPE_AGG);
140 #endif
141 }
142 
GetXFAEntryFromDocument(const CPDF_Document * doc)143 RetainPtr<const CPDF_Object> GetXFAEntryFromDocument(const CPDF_Document* doc) {
144   const CPDF_Dictionary* root = doc->GetRoot();
145   if (!root)
146     return nullptr;
147 
148   RetainPtr<const CPDF_Dictionary> acro_form = root->GetDictFor("AcroForm");
149   return acro_form ? acro_form->GetObjectFor("XFA") : nullptr;
150 }
151 
152 struct XFAPacket {
153   ByteString name;
154   RetainPtr<const CPDF_Stream> data;
155 };
156 
GetXFAPackets(RetainPtr<const CPDF_Object> xfa_object)157 std::vector<XFAPacket> GetXFAPackets(RetainPtr<const CPDF_Object> xfa_object) {
158   std::vector<XFAPacket> packets;
159 
160   if (!xfa_object)
161     return packets;
162 
163   RetainPtr<const CPDF_Stream> xfa_stream = ToStream(xfa_object->GetDirect());
164   if (xfa_stream) {
165     packets.push_back({"", std::move(xfa_stream)});
166     return packets;
167   }
168 
169   RetainPtr<const CPDF_Array> xfa_array = ToArray(xfa_object->GetDirect());
170   if (!xfa_array)
171     return packets;
172 
173   packets.reserve(1 + (xfa_array->size() / 2));
174   for (size_t i = 0; i < xfa_array->size(); i += 2) {
175     if (i + 1 == xfa_array->size())
176       break;
177 
178     RetainPtr<const CPDF_String> name = xfa_array->GetStringAt(i);
179     if (!name)
180       continue;
181 
182     RetainPtr<const CPDF_Stream> data = xfa_array->GetStreamAt(i + 1);
183     if (!data)
184       continue;
185 
186     packets.push_back({name->GetString(), std::move(data)});
187   }
188   return packets;
189 }
190 
LoadDocumentImpl(RetainPtr<IFX_SeekableReadStream> pFileAccess,FPDF_BYTESTRING password)191 FPDF_DOCUMENT LoadDocumentImpl(RetainPtr<IFX_SeekableReadStream> pFileAccess,
192                                FPDF_BYTESTRING password) {
193   if (!pFileAccess) {
194     ProcessParseError(CPDF_Parser::FILE_ERROR);
195     return nullptr;
196   }
197 
198   auto pDocument =
199       std::make_unique<CPDF_Document>(std::make_unique<CPDF_DocRenderData>(),
200                                       std::make_unique<CPDF_DocPageData>());
201 
202   CPDF_Parser::Error error =
203       pDocument->LoadDoc(std::move(pFileAccess), password);
204   if (error != CPDF_Parser::SUCCESS) {
205     ProcessParseError(error);
206     return nullptr;
207   }
208 
209   ReportUnsupportedFeatures(pDocument.get());
210   return FPDFDocumentFromCPDFDocument(pDocument.release());
211 }
212 
213 }  // namespace
214 
FPDF_InitLibrary()215 FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary() {
216   FPDF_InitLibraryWithConfig(nullptr);
217 }
218 
219 FPDF_EXPORT void FPDF_CALLCONV
FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG * config)220 FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG* config) {
221   if (g_bLibraryInitialized)
222     return;
223 
224   FX_InitializeMemoryAllocators();
225   CFX_GEModule::Create(config ? config->m_pUserFontPaths : nullptr);
226   CPDF_PageModule::Create();
227 
228 #ifdef PDF_ENABLE_XFA
229   CPDFXFA_ModuleInit();
230 #endif  // PDF_ENABLE_XFA
231 
232   if (config && config->version >= 2) {
233     void* platform = config->version >= 3 ? config->m_pPlatform : nullptr;
234     IJS_Runtime::Initialize(config->m_v8EmbedderSlot, config->m_pIsolate,
235                             platform);
236 
237     if (config->version >= 4)
238       UseRendererType(config->m_RendererType);
239   }
240   g_bLibraryInitialized = true;
241 }
242 
FPDF_DestroyLibrary()243 FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyLibrary() {
244   if (!g_bLibraryInitialized)
245     return;
246 
247 #ifdef PDF_ENABLE_XFA
248   CPDFXFA_ModuleDestroy();
249 #endif  // PDF_ENABLE_XFA
250 
251   CPDF_PageModule::Destroy();
252   CFX_GEModule::Destroy();
253   IJS_Runtime::Destroy();
254 
255   g_bLibraryInitialized = false;
256 }
257 
FPDF_SetSandBoxPolicy(FPDF_DWORD policy,FPDF_BOOL enable)258 FPDF_EXPORT void FPDF_CALLCONV FPDF_SetSandBoxPolicy(FPDF_DWORD policy,
259                                                      FPDF_BOOL enable) {
260   return SetPDFSandboxPolicy(policy, enable);
261 }
262 
263 #if BUILDFLAG(IS_WIN)
FPDF_SetPrintMode(int mode)264 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode) {
265   if (mode < FPDF_PRINTMODE_EMF ||
266       mode > FPDF_PRINTMODE_POSTSCRIPT3_TYPE42_PASSTHROUGH) {
267     return FALSE;
268   }
269 
270   g_pdfium_print_mode = static_cast<WindowsPrintMode>(mode);
271   return TRUE;
272 }
273 #endif  // BUILDFLAG(IS_WIN)
274 
275 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_LoadDocument(FPDF_STRING file_path,FPDF_BYTESTRING password)276 FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password) {
277   // NOTE: the creation of the file needs to be by the embedder on the
278   // other side of this API.
279   return LoadDocumentImpl(IFX_SeekableReadStream::CreateFromFilename(file_path),
280                           password);
281 }
282 
FPDF_GetFormType(FPDF_DOCUMENT document)283 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetFormType(FPDF_DOCUMENT document) {
284   const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
285   if (!pDoc)
286     return FORMTYPE_NONE;
287 
288   const CPDF_Dictionary* pRoot = pDoc->GetRoot();
289   if (!pRoot)
290     return FORMTYPE_NONE;
291 
292   RetainPtr<const CPDF_Dictionary> pAcroForm = pRoot->GetDictFor("AcroForm");
293   if (!pAcroForm)
294     return FORMTYPE_NONE;
295 
296   RetainPtr<const CPDF_Object> pXFA = pAcroForm->GetObjectFor("XFA");
297   if (!pXFA)
298     return FORMTYPE_ACRO_FORM;
299 
300   bool bNeedsRendering = pRoot->GetBooleanFor("NeedsRendering", false);
301   return bNeedsRendering ? FORMTYPE_XFA_FULL : FORMTYPE_XFA_FOREGROUND;
302 }
303 
FPDF_LoadXFA(FPDF_DOCUMENT document)304 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_LoadXFA(FPDF_DOCUMENT document) {
305 #ifdef PDF_ENABLE_XFA
306   auto* pDoc = CPDFDocumentFromFPDFDocument(document);
307   if (!pDoc)
308     return false;
309 
310   auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
311   if (pContext)
312     return pContext->LoadXFADoc();
313 #endif  // PDF_ENABLE_XFA
314   return false;
315 }
316 
317 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_LoadMemDocument(const void * data_buf,int size,FPDF_BYTESTRING password)318 FPDF_LoadMemDocument(const void* data_buf, int size, FPDF_BYTESTRING password) {
319   return LoadDocumentImpl(
320       pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
321           pdfium::make_span(static_cast<const uint8_t*>(data_buf), size)),
322       password);
323 }
324 
325 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_LoadMemDocument64(const void * data_buf,size_t size,FPDF_BYTESTRING password)326 FPDF_LoadMemDocument64(const void* data_buf,
327                        size_t size,
328                        FPDF_BYTESTRING password) {
329   return LoadDocumentImpl(
330       pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
331           pdfium::make_span(static_cast<const uint8_t*>(data_buf), size)),
332       password);
333 }
334 
335 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_LoadCustomDocument(FPDF_FILEACCESS * pFileAccess,FPDF_BYTESTRING password)336 FPDF_LoadCustomDocument(FPDF_FILEACCESS* pFileAccess,
337                         FPDF_BYTESTRING password) {
338   if (!pFileAccess)
339     return nullptr;
340   return LoadDocumentImpl(pdfium::MakeRetain<CPDFSDK_CustomAccess>(pFileAccess),
341                           password);
342 }
343 
FPDF_GetFileVersion(FPDF_DOCUMENT doc,int * fileVersion)344 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetFileVersion(FPDF_DOCUMENT doc,
345                                                         int* fileVersion) {
346   if (!fileVersion)
347     return false;
348 
349   *fileVersion = 0;
350   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(doc);
351   if (!pDoc)
352     return false;
353 
354   const CPDF_Parser* pParser = pDoc->GetParser();
355   if (!pParser)
356     return false;
357 
358   *fileVersion = pParser->GetFileVersion();
359   return true;
360 }
361 
362 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_DocumentHasValidCrossReferenceTable(FPDF_DOCUMENT document)363 FPDF_DocumentHasValidCrossReferenceTable(FPDF_DOCUMENT document) {
364   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
365   return pDoc && pDoc->has_valid_cross_reference_table();
366 }
367 
368 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetDocPermissions(FPDF_DOCUMENT document)369 FPDF_GetDocPermissions(FPDF_DOCUMENT document) {
370   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
371   return pDoc ? pDoc->GetUserPermissions() : 0;
372 }
373 
374 FPDF_EXPORT int FPDF_CALLCONV
FPDF_GetSecurityHandlerRevision(FPDF_DOCUMENT document)375 FPDF_GetSecurityHandlerRevision(FPDF_DOCUMENT document) {
376   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
377   if (!pDoc || !pDoc->GetParser())
378     return -1;
379 
380   RetainPtr<const CPDF_Dictionary> pDict = pDoc->GetParser()->GetEncryptDict();
381   return pDict ? pDict->GetIntegerFor("R") : -1;
382 }
383 
FPDF_GetPageCount(FPDF_DOCUMENT document)384 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageCount(FPDF_DOCUMENT document) {
385   auto* pDoc = CPDFDocumentFromFPDFDocument(document);
386   if (!pDoc)
387     return 0;
388 
389   auto* pExtension = pDoc->GetExtension();
390   return pExtension ? pExtension->GetPageCount() : pDoc->GetPageCount();
391 }
392 
FPDF_LoadPage(FPDF_DOCUMENT document,int page_index)393 FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDF_LoadPage(FPDF_DOCUMENT document,
394                                                   int page_index) {
395   auto* pDoc = CPDFDocumentFromFPDFDocument(document);
396   if (!pDoc)
397     return nullptr;
398 
399   if (page_index < 0 || page_index >= FPDF_GetPageCount(document))
400     return nullptr;
401 
402 #ifdef PDF_ENABLE_XFA
403   auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
404   if (pContext) {
405     return FPDFPageFromIPDFPage(
406         pContext->GetOrCreateXFAPage(page_index).Leak());
407   }
408 #endif  // PDF_ENABLE_XFA
409 
410   RetainPtr<CPDF_Dictionary> pDict = pDoc->GetMutablePageDictionary(page_index);
411   if (!pDict)
412     return nullptr;
413 
414   auto pPage = pdfium::MakeRetain<CPDF_Page>(pDoc, std::move(pDict));
415   pPage->AddPageImageCache();
416   pPage->ParseContent();
417 
418   return FPDFPageFromIPDFPage(pPage.Leak());
419 }
420 
FPDF_GetPageWidthF(FPDF_PAGE page)421 FPDF_EXPORT float FPDF_CALLCONV FPDF_GetPageWidthF(FPDF_PAGE page) {
422   IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
423   return pPage ? pPage->GetPageWidth() : 0.0f;
424 }
425 
FPDF_GetPageWidth(FPDF_PAGE page)426 FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageWidth(FPDF_PAGE page) {
427   return FPDF_GetPageWidthF(page);
428 }
429 
FPDF_GetPageHeightF(FPDF_PAGE page)430 FPDF_EXPORT float FPDF_CALLCONV FPDF_GetPageHeightF(FPDF_PAGE page) {
431   IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
432   return pPage ? pPage->GetPageHeight() : 0.0f;
433 }
434 
FPDF_GetPageHeight(FPDF_PAGE page)435 FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageHeight(FPDF_PAGE page) {
436   return FPDF_GetPageHeightF(page);
437 }
438 
FPDF_GetPageBoundingBox(FPDF_PAGE page,FS_RECTF * rect)439 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetPageBoundingBox(FPDF_PAGE page,
440                                                             FS_RECTF* rect) {
441   if (!rect)
442     return false;
443 
444   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
445   if (!pPage)
446     return false;
447 
448   *rect = FSRectFFromCFXFloatRect(pPage->GetBBox());
449   return true;
450 }
451 
452 #if BUILDFLAG(IS_WIN)
453 namespace {
454 
455 constexpr float kEpsilonSize = 0.01f;
456 
IsPageTooSmall(const CPDF_Page * page)457 bool IsPageTooSmall(const CPDF_Page* page) {
458   const CFX_SizeF& page_size = page->GetPageSize();
459   return page_size.width < kEpsilonSize || page_size.height < kEpsilonSize;
460 }
461 
IsScalingTooSmall(const CFX_Matrix & matrix)462 bool IsScalingTooSmall(const CFX_Matrix& matrix) {
463   float horizontal;
464   float vertical;
465   if (matrix.a == 0.0f && matrix.d == 0.0f) {
466     horizontal = matrix.b;
467     vertical = matrix.c;
468   } else {
469     horizontal = matrix.a;
470     vertical = matrix.d;
471   }
472   return fabsf(horizontal) < kEpsilonSize || fabsf(vertical) < kEpsilonSize;
473 }
474 
475 // Get a bitmap of just the mask section defined by |mask_box| from a full page
476 // bitmap |pBitmap|.
GetMaskBitmap(CPDF_Page * pPage,int start_x,int start_y,int size_x,int size_y,int rotate,const RetainPtr<CFX_DIBitmap> & pSrc,const CFX_FloatRect & mask_box,FX_RECT * bitmap_area)477 RetainPtr<CFX_DIBitmap> GetMaskBitmap(CPDF_Page* pPage,
478                                       int start_x,
479                                       int start_y,
480                                       int size_x,
481                                       int size_y,
482                                       int rotate,
483                                       const RetainPtr<CFX_DIBitmap>& pSrc,
484                                       const CFX_FloatRect& mask_box,
485                                       FX_RECT* bitmap_area) {
486   if (IsPageTooSmall(pPage))
487     return nullptr;
488 
489   FX_RECT page_rect(start_x, start_y, start_x + size_x, start_y + size_y);
490   CFX_Matrix matrix = pPage->GetDisplayMatrix(page_rect, rotate);
491   if (IsScalingTooSmall(matrix))
492     return nullptr;
493 
494   *bitmap_area = matrix.TransformRect(mask_box).GetOuterRect();
495   if (bitmap_area->IsEmpty())
496     return nullptr;
497 
498   // Create a new bitmap to transfer part of the page bitmap to.
499   RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
500   if (!pDst->Create(bitmap_area->Width(), bitmap_area->Height(),
501                     FXDIB_Format::kArgb)) {
502     return nullptr;
503   }
504   pDst->Clear(0x00ffffff);
505   pDst->TransferBitmap(0, 0, bitmap_area->Width(), bitmap_area->Height(), pSrc,
506                        bitmap_area->left, bitmap_area->top);
507   return pDst;
508 }
509 
RenderBitmap(CFX_RenderDevice * device,const RetainPtr<CFX_DIBitmap> & pSrc,const FX_RECT & mask_area)510 void RenderBitmap(CFX_RenderDevice* device,
511                   const RetainPtr<CFX_DIBitmap>& pSrc,
512                   const FX_RECT& mask_area) {
513   int size_x_bm = mask_area.Width();
514   int size_y_bm = mask_area.Height();
515   if (size_x_bm == 0 || size_y_bm == 0)
516     return;
517 
518   // Create a new bitmap from the old one
519   RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
520   if (!pDst->Create(size_x_bm, size_y_bm, FXDIB_Format::kRgb32))
521     return;
522 
523   pDst->Clear(0xffffffff);
524   pDst->CompositeBitmap(0, 0, size_x_bm, size_y_bm, pSrc, 0, 0,
525                         BlendMode::kNormal, nullptr, false);
526 
527   if (device->GetDeviceType() == DeviceType::kPrinter) {
528     device->StretchDIBits(pDst, mask_area.left, mask_area.top, size_x_bm,
529                           size_y_bm);
530   } else {
531     device->SetDIBits(pDst, mask_area.left, mask_area.top);
532   }
533 }
534 
535 }  // namespace
536 
FPDF_RenderPage(HDC dc,FPDF_PAGE page,int start_x,int start_y,int size_x,int size_y,int rotate,int flags)537 FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc,
538                                                FPDF_PAGE page,
539                                                int start_x,
540                                                int start_y,
541                                                int size_x,
542                                                int size_y,
543                                                int rotate,
544                                                int flags) {
545   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
546   if (!pPage)
547     return;
548 
549   auto owned_context = std::make_unique<CPDF_PageRenderContext>();
550   CPDF_PageRenderContext* context = owned_context.get();
551   CPDF_Page::RenderContextClearer clearer(pPage);
552   pPage->SetRenderContext(std::move(owned_context));
553 
554   // Don't render the full page to bitmap for a mask unless there are a lot
555   // of masks. Full page bitmaps result in large spool sizes, so they should
556   // only be used when necessary. For large numbers of masks, rendering each
557   // individually is inefficient and unlikely to significantly improve spool
558   // size.
559   const bool bEnableImageMasks =
560       g_pdfium_print_mode == WindowsPrintMode::kEmfImageMasks;
561   const bool bNewBitmap = pPage->BackgroundAlphaNeeded() ||
562                           (pPage->HasImageMask() && !bEnableImageMasks) ||
563                           pPage->GetMaskBoundingBoxes().size() > 100;
564   const bool bHasMask = pPage->HasImageMask() && !bNewBitmap;
565   auto* render_data = CPDF_DocRenderData::FromDocument(pPage->GetDocument());
566   if (!bNewBitmap && !bHasMask) {
567     context->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
568         dc, render_data->GetPSFontTracker());
569     CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
570                                   size_y, rotate, flags,
571                                   /*color_scheme=*/nullptr,
572                                   /*need_to_restore=*/true, /*pause=*/nullptr);
573     return;
574   }
575 
576   RetainPtr<CFX_DIBitmap> pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
577   // Create will probably work fine even if it fails here: we will just attach
578   // a zero-sized bitmap to `device`.
579   pBitmap->Create(size_x, size_y, FXDIB_Format::kArgb);
580   pBitmap->Clear(0x00ffffff);
581   auto device = std::make_unique<CFX_DefaultRenderDevice>();
582   device->Attach(pBitmap);
583   context->m_pDevice = std::move(device);
584   if (bHasMask) {
585     context->m_pOptions = std::make_unique<CPDF_RenderOptions>();
586     context->m_pOptions->GetOptions().bBreakForMasks = true;
587   }
588 
589   CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
590                                 size_y, rotate, flags, /*color_scheme=*/nullptr,
591                                 /*need_to_restore=*/true,
592                                 /*pause=*/nullptr);
593 
594   if (!bHasMask) {
595     CPDF_WindowsRenderDevice win_dc(dc, render_data->GetPSFontTracker());
596     bool bitsStretched = false;
597     if (win_dc.GetDeviceType() == DeviceType::kPrinter) {
598       auto pDst = pdfium::MakeRetain<CFX_DIBitmap>();
599       if (pDst->Create(size_x, size_y, FXDIB_Format::kRgb32)) {
600         fxcrt::spanset(
601             pDst->GetWritableBuffer().first(pBitmap->GetPitch() * size_y), -1);
602         pDst->CompositeBitmap(0, 0, size_x, size_y, pBitmap, 0, 0,
603                               BlendMode::kNormal, nullptr, false);
604         win_dc.StretchDIBits(pDst, 0, 0, size_x, size_y);
605         bitsStretched = true;
606       }
607     }
608     if (!bitsStretched)
609       win_dc.SetDIBits(pBitmap, 0, 0);
610     return;
611   }
612 
613   // Finish rendering the page to bitmap and copy the correct segments
614   // of the page to individual image mask bitmaps.
615   const std::vector<CFX_FloatRect>& mask_boxes = pPage->GetMaskBoundingBoxes();
616   std::vector<FX_RECT> bitmap_areas(mask_boxes.size());
617   std::vector<RetainPtr<CFX_DIBitmap>> bitmaps(mask_boxes.size());
618   for (size_t i = 0; i < mask_boxes.size(); i++) {
619     bitmaps[i] = GetMaskBitmap(pPage, start_x, start_y, size_x, size_y, rotate,
620                                pBitmap, mask_boxes[i], &bitmap_areas[i]);
621     context->m_pRenderer->Continue(nullptr);
622   }
623 
624   // Begin rendering to the printer. Add flag to indicate the renderer should
625   // pause after each image mask.
626   pPage->ClearRenderContext();
627   owned_context = std::make_unique<CPDF_PageRenderContext>();
628   context = owned_context.get();
629   pPage->SetRenderContext(std::move(owned_context));
630   context->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
631       dc, render_data->GetPSFontTracker());
632   context->m_pOptions = std::make_unique<CPDF_RenderOptions>();
633   context->m_pOptions->GetOptions().bBreakForMasks = true;
634 
635   CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
636                                 size_y, rotate, flags, /*color_scheme=*/nullptr,
637                                 /*need_to_restore=*/true,
638                                 /*pause=*/nullptr);
639 
640   // Render masks
641   for (size_t i = 0; i < mask_boxes.size(); i++) {
642     // Render the bitmap for the mask and free the bitmap.
643     if (bitmaps[i]) {  // will be null if mask has zero area
644       RenderBitmap(context->m_pDevice.get(), bitmaps[i], bitmap_areas[i]);
645     }
646     // Render the next portion of page.
647     context->m_pRenderer->Continue(nullptr);
648   }
649 }
650 #endif  // BUILDFLAG(IS_WIN)
651 
FPDF_RenderPageBitmap(FPDF_BITMAP bitmap,FPDF_PAGE page,int start_x,int start_y,int size_x,int size_y,int rotate,int flags)652 FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageBitmap(FPDF_BITMAP bitmap,
653                                                      FPDF_PAGE page,
654                                                      int start_x,
655                                                      int start_y,
656                                                      int size_x,
657                                                      int size_y,
658                                                      int rotate,
659                                                      int flags) {
660   if (!bitmap)
661     return;
662 
663   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
664   if (!pPage)
665     return;
666 
667   auto owned_context = std::make_unique<CPDF_PageRenderContext>();
668   CPDF_PageRenderContext* context = owned_context.get();
669   CPDF_Page::RenderContextClearer clearer(pPage);
670   pPage->SetRenderContext(std::move(owned_context));
671 
672   RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
673   auto device = std::make_unique<CFX_DefaultRenderDevice>();
674   device->AttachWithRgbByteOrder(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER));
675   context->m_pDevice = std::move(device);
676 
677   CPDFSDK_RenderPageWithContext(context, pPage, start_x, start_y, size_x,
678                                 size_y, rotate, flags, /*color_scheme=*/nullptr,
679                                 /*need_to_restore=*/true,
680                                 /*pause=*/nullptr);
681 
682 #if defined(_SKIA_SUPPORT_)
683   if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
684     pBitmap->UnPreMultiply();
685   }
686 #endif
687 }
688 
689 FPDF_EXPORT void FPDF_CALLCONV
FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap,FPDF_PAGE page,const FS_MATRIX * matrix,const FS_RECTF * clipping,int flags)690 FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap,
691                                 FPDF_PAGE page,
692                                 const FS_MATRIX* matrix,
693                                 const FS_RECTF* clipping,
694                                 int flags) {
695   if (!bitmap)
696     return;
697 
698   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
699   if (!pPage)
700     return;
701 
702   auto owned_context = std::make_unique<CPDF_PageRenderContext>();
703   CPDF_PageRenderContext* context = owned_context.get();
704   CPDF_Page::RenderContextClearer clearer(pPage);
705   pPage->SetRenderContext(std::move(owned_context));
706 
707   RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
708   auto device = std::make_unique<CFX_DefaultRenderDevice>();
709   device->AttachWithRgbByteOrder(std::move(pBitmap),
710                                  !!(flags & FPDF_REVERSE_BYTE_ORDER));
711   context->m_pDevice = std::move(device);
712 
713   CFX_FloatRect clipping_rect;
714   if (clipping)
715     clipping_rect = CFXFloatRectFromFSRectF(*clipping);
716   FX_RECT clip_rect = clipping_rect.ToFxRect();
717 
718   const FX_RECT rect(0, 0, pPage->GetPageWidth(), pPage->GetPageHeight());
719   CFX_Matrix transform_matrix = pPage->GetDisplayMatrix(rect, 0);
720   if (matrix)
721     transform_matrix *= CFXMatrixFromFSMatrix(*matrix);
722   CPDFSDK_RenderPage(context, pPage, transform_matrix, clip_rect, flags,
723                      /*color_scheme=*/nullptr);
724 }
725 
726 #if defined(_SKIA_SUPPORT_)
FPDF_RenderPageSkia(FPDF_SKIA_CANVAS canvas,FPDF_PAGE page,int size_x,int size_y)727 FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageSkia(FPDF_SKIA_CANVAS canvas,
728                                                    FPDF_PAGE page,
729                                                    int size_x,
730                                                    int size_y) {
731   if (!canvas) {
732     return;
733   }
734 
735   CPDF_Page* cpdf_page = CPDFPageFromFPDFPage(page);
736   if (!cpdf_page) {
737     return;
738   }
739 
740   auto owned_context = std::make_unique<CPDF_PageRenderContext>();
741   CPDF_PageRenderContext* context = owned_context.get();
742   CPDF_Page::RenderContextClearer clearer(cpdf_page);
743   cpdf_page->SetRenderContext(std::move(owned_context));
744 
745   auto device = std::make_unique<CFX_DefaultRenderDevice>();
746   device->AttachCanvas(reinterpret_cast<SkCanvas*>(canvas));
747   context->m_pDevice = std::move(device);
748 
749   CPDFSDK_RenderPageWithContext(context, cpdf_page, 0, 0, size_x, size_y, 0, 0,
750                                 /*color_scheme=*/nullptr,
751                                 /*need_to_restore=*/true, /*pause=*/nullptr);
752 }
753 #endif  // defined(_SKIA_SUPPORT_)
754 
FPDF_ClosePage(FPDF_PAGE page)755 FPDF_EXPORT void FPDF_CALLCONV FPDF_ClosePage(FPDF_PAGE page) {
756   if (!page)
757     return;
758 
759   // Take it back across the API and hold for duration of this function.
760   RetainPtr<IPDF_Page> pPage;
761   pPage.Unleak(IPDFPageFromFPDFPage(page));
762 
763   if (pPage->AsXFAPage())
764     return;
765 
766   // This will delete the PageView object corresponding to |pPage|. We must
767   // cleanup the PageView before releasing the reference on |pPage| as it will
768   // attempt to reset the PageView during destruction.
769   pPage->AsPDFPage()->ClearView();
770 }
771 
FPDF_CloseDocument(FPDF_DOCUMENT document)772 FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseDocument(FPDF_DOCUMENT document) {
773   // Take it back across the API and throw it away,
774   std::unique_ptr<CPDF_Document>(CPDFDocumentFromFPDFDocument(document));
775 }
776 
FPDF_GetLastError()777 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError() {
778   return FXSYS_GetLastError();
779 }
780 
FPDF_DeviceToPage(FPDF_PAGE page,int start_x,int start_y,int size_x,int size_y,int rotate,int device_x,int device_y,double * page_x,double * page_y)781 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_DeviceToPage(FPDF_PAGE page,
782                                                       int start_x,
783                                                       int start_y,
784                                                       int size_x,
785                                                       int size_y,
786                                                       int rotate,
787                                                       int device_x,
788                                                       int device_y,
789                                                       double* page_x,
790                                                       double* page_y) {
791   if (!page || !page_x || !page_y)
792     return false;
793 
794   IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
795   const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
796   absl::optional<CFX_PointF> pos =
797       pPage->DeviceToPage(rect, rotate, CFX_PointF(device_x, device_y));
798   if (!pos.has_value())
799     return false;
800 
801   *page_x = pos->x;
802   *page_y = pos->y;
803   return true;
804 }
805 
FPDF_PageToDevice(FPDF_PAGE page,int start_x,int start_y,int size_x,int size_y,int rotate,double page_x,double page_y,int * device_x,int * device_y)806 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_PageToDevice(FPDF_PAGE page,
807                                                       int start_x,
808                                                       int start_y,
809                                                       int size_x,
810                                                       int size_y,
811                                                       int rotate,
812                                                       double page_x,
813                                                       double page_y,
814                                                       int* device_x,
815                                                       int* device_y) {
816   if (!page || !device_x || !device_y)
817     return false;
818 
819   IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
820   const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
821   CFX_PointF page_point(static_cast<float>(page_x), static_cast<float>(page_y));
822   absl::optional<CFX_PointF> pos =
823       pPage->PageToDevice(rect, rotate, page_point);
824   if (!pos.has_value())
825     return false;
826 
827   *device_x = FXSYS_roundf(pos->x);
828   *device_y = FXSYS_roundf(pos->y);
829   return true;
830 }
831 
FPDFBitmap_Create(int width,int height,int alpha)832 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width,
833                                                         int height,
834                                                         int alpha) {
835   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
836   if (!pBitmap->Create(width, height,
837                        alpha ? FXDIB_Format::kArgb : FXDIB_Format::kRgb32)) {
838     return nullptr;
839   }
840   return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
841 }
842 
FPDFBitmap_CreateEx(int width,int height,int format,void * first_scan,int stride)843 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width,
844                                                           int height,
845                                                           int format,
846                                                           void* first_scan,
847                                                           int stride) {
848   FXDIB_Format fx_format;
849   switch (format) {
850     case FPDFBitmap_Gray:
851       fx_format = FXDIB_Format::k8bppRgb;
852       break;
853     case FPDFBitmap_BGR:
854       fx_format = FXDIB_Format::kRgb;
855       break;
856     case FPDFBitmap_BGRx:
857       fx_format = FXDIB_Format::kRgb32;
858       break;
859     case FPDFBitmap_BGRA:
860       fx_format = FXDIB_Format::kArgb;
861       break;
862     default:
863       return nullptr;
864   }
865 
866   // Ensure external memory is good at least for the duration of this call.
867   UnownedPtr<uint8_t> pChecker(static_cast<uint8_t*>(first_scan));
868   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
869   if (!pBitmap->Create(width, height, fx_format, pChecker, stride))
870     return nullptr;
871 
872   return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
873 }
874 
FPDFBitmap_GetFormat(FPDF_BITMAP bitmap)875 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetFormat(FPDF_BITMAP bitmap) {
876   if (!bitmap)
877     return FPDFBitmap_Unknown;
878 
879   FXDIB_Format format = CFXDIBitmapFromFPDFBitmap(bitmap)->GetFormat();
880   switch (format) {
881     case FXDIB_Format::k8bppRgb:
882     case FXDIB_Format::k8bppMask:
883       return FPDFBitmap_Gray;
884     case FXDIB_Format::kRgb:
885       return FPDFBitmap_BGR;
886     case FXDIB_Format::kRgb32:
887       return FPDFBitmap_BGRx;
888     case FXDIB_Format::kArgb:
889       return FPDFBitmap_BGRA;
890     default:
891       return FPDFBitmap_Unknown;
892   }
893 }
894 
FPDFBitmap_FillRect(FPDF_BITMAP bitmap,int left,int top,int width,int height,FPDF_DWORD color)895 FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_FillRect(FPDF_BITMAP bitmap,
896                                                    int left,
897                                                    int top,
898                                                    int width,
899                                                    int height,
900                                                    FPDF_DWORD color) {
901   if (!bitmap)
902     return;
903 
904   CFX_DefaultRenderDevice device;
905   RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
906   device.Attach(pBitmap);
907   if (!pBitmap->IsAlphaFormat())
908     color |= 0xFF000000;
909   device.FillRect(FX_RECT(left, top, left + width, top + height),
910                   static_cast<uint32_t>(color));
911 }
912 
FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap)913 FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {
914   return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetWritableBuffer().data()
915                 : nullptr;
916 }
917 
FPDFBitmap_GetWidth(FPDF_BITMAP bitmap)918 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetWidth(FPDF_BITMAP bitmap) {
919   return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetWidth() : 0;
920 }
921 
FPDFBitmap_GetHeight(FPDF_BITMAP bitmap)922 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetHeight(FPDF_BITMAP bitmap) {
923   return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetHeight() : 0;
924 }
925 
FPDFBitmap_GetStride(FPDF_BITMAP bitmap)926 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetStride(FPDF_BITMAP bitmap) {
927   return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetPitch() : 0;
928 }
929 
FPDFBitmap_Destroy(FPDF_BITMAP bitmap)930 FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_Destroy(FPDF_BITMAP bitmap) {
931   RetainPtr<CFX_DIBitmap> destroyer;
932   destroyer.Unleak(CFXDIBitmapFromFPDFBitmap(bitmap));
933 }
934 
935 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document,int page_index,FS_SIZEF * size)936 FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document,
937                          int page_index,
938                          FS_SIZEF* size) {
939   if (!size)
940     return false;
941 
942   auto* pDoc = CPDFDocumentFromFPDFDocument(document);
943   if (!pDoc)
944     return false;
945 
946 #ifdef PDF_ENABLE_XFA
947   if (page_index < 0 || page_index >= FPDF_GetPageCount(document))
948     return false;
949 
950   auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
951   if (pContext) {
952     RetainPtr<CPDFXFA_Page> pPage = pContext->GetOrCreateXFAPage(page_index);
953     if (!pPage)
954       return false;
955 
956     size->width = pPage->GetPageWidth();
957     size->height = pPage->GetPageHeight();
958     return true;
959   }
960 #endif  // PDF_ENABLE_XFA
961 
962   RetainPtr<CPDF_Dictionary> pDict = pDoc->GetMutablePageDictionary(page_index);
963   if (!pDict)
964     return false;
965 
966   auto page = pdfium::MakeRetain<CPDF_Page>(pDoc, std::move(pDict));
967   page->AddPageImageCache();
968   size->width = page->GetPageWidth();
969   size->height = page->GetPageHeight();
970   return true;
971 }
972 
FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,int page_index,double * width,double * height)973 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,
974                                                       int page_index,
975                                                       double* width,
976                                                       double* height) {
977   if (!width || !height)
978     return false;
979 
980   FS_SIZEF size;
981   if (!FPDF_GetPageSizeByIndexF(document, page_index, &size))
982     return false;
983 
984   *width = size.width;
985   *height = size.height;
986   return true;
987 }
988 
989 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document)990 FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document) {
991   const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
992   if (!pDoc)
993     return true;
994   CPDF_ViewerPreferences viewRef(pDoc);
995   return viewRef.PrintScaling();
996 }
997 
998 FPDF_EXPORT int FPDF_CALLCONV
FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document)999 FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document) {
1000   const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1001   if (!pDoc)
1002     return 1;
1003   CPDF_ViewerPreferences viewRef(pDoc);
1004   return viewRef.NumCopies();
1005 }
1006 
1007 FPDF_EXPORT FPDF_PAGERANGE FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document)1008 FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document) {
1009   const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1010   if (!pDoc)
1011     return nullptr;
1012   CPDF_ViewerPreferences viewRef(pDoc);
1013 
1014   // Unretained reference in public API. NOLINTNEXTLINE
1015   return FPDFPageRangeFromCPDFArray(viewRef.PrintPageRange());
1016 }
1017 
1018 FPDF_EXPORT size_t FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange)1019 FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange) {
1020   const CPDF_Array* pArray = CPDFArrayFromFPDFPageRange(pagerange);
1021   return pArray ? pArray->size() : 0;
1022 }
1023 
1024 FPDF_EXPORT int FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRangeElement(FPDF_PAGERANGE pagerange,size_t index)1025 FPDF_VIEWERREF_GetPrintPageRangeElement(FPDF_PAGERANGE pagerange,
1026                                         size_t index) {
1027   const CPDF_Array* pArray = CPDFArrayFromFPDFPageRange(pagerange);
1028   if (!pArray || index >= pArray->size())
1029     return -1;
1030   return pArray->GetIntegerAt(index);
1031 }
1032 
1033 FPDF_EXPORT FPDF_DUPLEXTYPE FPDF_CALLCONV
FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document)1034 FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document) {
1035   const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1036   if (!pDoc)
1037     return DuplexUndefined;
1038   CPDF_ViewerPreferences viewRef(pDoc);
1039   ByteString duplex = viewRef.Duplex();
1040   if ("Simplex" == duplex)
1041     return Simplex;
1042   if ("DuplexFlipShortEdge" == duplex)
1043     return DuplexFlipShortEdge;
1044   if ("DuplexFlipLongEdge" == duplex)
1045     return DuplexFlipLongEdge;
1046   return DuplexUndefined;
1047 }
1048 
1049 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,FPDF_BYTESTRING key,char * buffer,unsigned long length)1050 FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,
1051                        FPDF_BYTESTRING key,
1052                        char* buffer,
1053                        unsigned long length) {
1054   const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1055   if (!pDoc)
1056     return 0;
1057 
1058   CPDF_ViewerPreferences viewRef(pDoc);
1059   absl::optional<ByteString> bsVal = viewRef.GenericName(key);
1060   if (!bsVal.has_value())
1061     return 0;
1062 
1063   return NulTerminateMaybeCopyAndReturnLength(bsVal.value(), buffer, length);
1064 }
1065 
1066 FPDF_EXPORT FPDF_DWORD FPDF_CALLCONV
FPDF_CountNamedDests(FPDF_DOCUMENT document)1067 FPDF_CountNamedDests(FPDF_DOCUMENT document) {
1068   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1069   if (!pDoc)
1070     return 0;
1071 
1072   const CPDF_Dictionary* pRoot = pDoc->GetRoot();
1073   if (!pRoot)
1074     return 0;
1075 
1076   auto name_tree = CPDF_NameTree::Create(pDoc, "Dests");
1077   FX_SAFE_UINT32 count = name_tree ? name_tree->GetCount() : 0;
1078   RetainPtr<const CPDF_Dictionary> pOldStyleDests = pRoot->GetDictFor("Dests");
1079   if (pOldStyleDests)
1080     count += pOldStyleDests->size();
1081   return count.ValueOrDefault(0);
1082 }
1083 
1084 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
FPDF_GetNamedDestByName(FPDF_DOCUMENT document,FPDF_BYTESTRING name)1085 FPDF_GetNamedDestByName(FPDF_DOCUMENT document, FPDF_BYTESTRING name) {
1086   if (!name || name[0] == 0)
1087     return nullptr;
1088 
1089   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1090   if (!pDoc)
1091     return nullptr;
1092 
1093   ByteString dest_name(name);
1094 
1095   // TODO(tsepez): murky ownership, should caller get a reference?
1096   // Unretained reference in public API. NOLINTNEXTLINE
1097   return FPDFDestFromCPDFArray(CPDF_NameTree::LookupNamedDest(pDoc, dest_name));
1098 }
1099 
1100 #ifdef PDF_ENABLE_V8
FPDF_GetRecommendedV8Flags()1101 FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags() {
1102   // Use interpreted JS only to avoid RWX pages in our address space. Also,
1103   // --jitless implies --no-expose-wasm, which reduce exposure since no PDF
1104   // should contain web assembly.
1105   return "--jitless";
1106 }
1107 
FPDF_GetArrayBufferAllocatorSharedInstance()1108 FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetArrayBufferAllocatorSharedInstance() {
1109   static pdfium::base::NoDestructor<CFX_V8ArrayBufferAllocator> allocator;
1110   return allocator.get();
1111 }
1112 #endif  // PDF_ENABLE_V8
1113 
1114 #ifdef PDF_ENABLE_XFA
FPDF_BStr_Init(FPDF_BSTR * bstr)1115 FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* bstr) {
1116   if (!bstr)
1117     return -1;
1118 
1119   bstr->str = nullptr;
1120   bstr->len = 0;
1121   return 0;
1122 }
1123 
FPDF_BStr_Set(FPDF_BSTR * bstr,const char * cstr,int length)1124 FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* bstr,
1125                                                     const char* cstr,
1126                                                     int length) {
1127   if (!bstr || !cstr)
1128     return -1;
1129 
1130   if (length == -1)
1131     length = pdfium::base::checked_cast<int>(strlen(cstr));
1132 
1133   if (length == 0) {
1134     FPDF_BStr_Clear(bstr);
1135     return 0;
1136   }
1137 
1138   if (bstr->str && bstr->len < length)
1139     bstr->str = FX_Realloc(char, bstr->str, length + 1);
1140   else if (!bstr->str)
1141     bstr->str = FX_Alloc(char, length + 1);
1142 
1143   bstr->str[length] = 0;
1144   memcpy(bstr->str, cstr, length);
1145   bstr->len = length;
1146   return 0;
1147 }
1148 
FPDF_BStr_Clear(FPDF_BSTR * bstr)1149 FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* bstr) {
1150   if (!bstr)
1151     return -1;
1152 
1153   if (bstr->str) {
1154     FX_Free(bstr->str);
1155     bstr->str = nullptr;
1156   }
1157   bstr->len = 0;
1158   return 0;
1159 }
1160 #endif  // PDF_ENABLE_XFA
1161 
FPDF_GetNamedDest(FPDF_DOCUMENT document,int index,void * buffer,long * buflen)1162 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document,
1163                                                       int index,
1164                                                       void* buffer,
1165                                                       long* buflen) {
1166   if (!buffer)
1167     *buflen = 0;
1168 
1169   if (index < 0)
1170     return nullptr;
1171 
1172   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1173   if (!pDoc)
1174     return nullptr;
1175 
1176   const CPDF_Dictionary* pRoot = pDoc->GetRoot();
1177   if (!pRoot)
1178     return nullptr;
1179 
1180   auto name_tree = CPDF_NameTree::Create(pDoc, "Dests");
1181   size_t name_tree_count = name_tree ? name_tree->GetCount() : 0;
1182   RetainPtr<const CPDF_Object> pDestObj;
1183   WideString wsName;
1184   if (static_cast<size_t>(index) >= name_tree_count) {
1185     // If |index| is out of bounds, then try to retrieve the Nth old style named
1186     // destination. Where N is 0-indexed, with N = index - name_tree_count.
1187     RetainPtr<const CPDF_Dictionary> pDest = pRoot->GetDictFor("Dests");
1188     if (!pDest)
1189       return nullptr;
1190 
1191     FX_SAFE_INT32 checked_count = name_tree_count;
1192     checked_count += pDest->size();
1193     if (!checked_count.IsValid() || index >= checked_count.ValueOrDie())
1194       return nullptr;
1195 
1196     index -= name_tree_count;
1197     int i = 0;
1198     ByteStringView bsName;
1199     CPDF_DictionaryLocker locker(pDest);
1200     for (const auto& it : locker) {
1201       bsName = it.first.AsStringView();
1202       pDestObj = it.second;
1203       if (i == index)
1204         break;
1205       i++;
1206     }
1207     wsName = PDF_DecodeText(bsName.raw_span());
1208   } else {
1209     pDestObj = name_tree->LookupValueAndName(index, &wsName);
1210   }
1211   if (!pDestObj)
1212     return nullptr;
1213   if (const CPDF_Dictionary* pDict = pDestObj->AsDictionary()) {
1214     pDestObj = pDict->GetArrayFor("D");
1215     if (!pDestObj)
1216       return nullptr;
1217   }
1218   if (!pDestObj->IsArray())
1219     return nullptr;
1220 
1221   ByteString utf16Name = wsName.ToUTF16LE();
1222   int len = pdfium::base::checked_cast<int>(utf16Name.GetLength());
1223   if (!buffer) {
1224     *buflen = len;
1225   } else if (len <= *buflen) {
1226     memcpy(buffer, utf16Name.c_str(), len);
1227     *buflen = len;
1228   } else {
1229     *buflen = -1;
1230   }
1231   return FPDFDestFromCPDFArray(pDestObj->AsArray());
1232 }
1233 
FPDF_GetXFAPacketCount(FPDF_DOCUMENT document)1234 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetXFAPacketCount(FPDF_DOCUMENT document) {
1235   CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
1236   if (!doc)
1237     return -1;
1238 
1239   return fxcrt::CollectionSize<int>(
1240       GetXFAPackets(GetXFAEntryFromDocument(doc)));
1241 }
1242 
1243 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetXFAPacketName(FPDF_DOCUMENT document,int index,void * buffer,unsigned long buflen)1244 FPDF_GetXFAPacketName(FPDF_DOCUMENT document,
1245                       int index,
1246                       void* buffer,
1247                       unsigned long buflen) {
1248   CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
1249   if (!doc || index < 0)
1250     return 0;
1251 
1252   std::vector<XFAPacket> xfa_packets =
1253       GetXFAPackets(GetXFAEntryFromDocument(doc));
1254   if (static_cast<size_t>(index) >= xfa_packets.size())
1255     return 0;
1256 
1257   return NulTerminateMaybeCopyAndReturnLength(xfa_packets[index].name, buffer,
1258                                               buflen);
1259 }
1260 
1261 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_GetXFAPacketContent(FPDF_DOCUMENT document,int index,void * buffer,unsigned long buflen,unsigned long * out_buflen)1262 FPDF_GetXFAPacketContent(FPDF_DOCUMENT document,
1263                          int index,
1264                          void* buffer,
1265                          unsigned long buflen,
1266                          unsigned long* out_buflen) {
1267   CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
1268   if (!doc || index < 0 || !out_buflen)
1269     return false;
1270 
1271   std::vector<XFAPacket> xfa_packets =
1272       GetXFAPackets(GetXFAEntryFromDocument(doc));
1273   if (static_cast<size_t>(index) >= xfa_packets.size())
1274     return false;
1275 
1276   *out_buflen = DecodeStreamMaybeCopyAndReturnLength(
1277       xfa_packets[index].data,
1278       {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
1279   return true;
1280 }
1281 
1282 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetTrailerEnds(FPDF_DOCUMENT document,unsigned int * buffer,unsigned long length)1283 FPDF_GetTrailerEnds(FPDF_DOCUMENT document,
1284                     unsigned int* buffer,
1285                     unsigned long length) {
1286   auto* doc = CPDFDocumentFromFPDFDocument(document);
1287   if (!doc)
1288     return 0;
1289 
1290   // Start recording trailer ends.
1291   auto* parser = doc->GetParser();
1292   std::vector<unsigned int> trailer_ends = parser->GetTrailerEnds();
1293   const unsigned long trailer_ends_len =
1294       fxcrt::CollectionSize<unsigned long>(trailer_ends);
1295   if (buffer && length >= trailer_ends_len) {
1296     for (size_t i = 0; i < trailer_ends_len; ++i)
1297       buffer[i] = trailer_ends[i];
1298   }
1299 
1300   return trailer_ends_len;
1301 }
1302