• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // Copyright 2014 PDFium Authors. All rights reserved.
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_pagemodule.h"
18  #include "core/fpdfapi/parser/cpdf_array.h"
19  #include "core/fpdfapi/parser/cpdf_dictionary.h"
20  #include "core/fpdfapi/parser/cpdf_document.h"
21  #include "core/fpdfapi/parser/cpdf_name.h"
22  #include "core/fpdfapi/parser/cpdf_parser.h"
23  #include "core/fpdfapi/parser/fpdf_parser_decode.h"
24  #include "core/fpdfapi/render/cpdf_docrenderdata.h"
25  #include "core/fpdfapi/render/cpdf_pagerendercache.h"
26  #include "core/fpdfapi/render/cpdf_pagerendercontext.h"
27  #include "core/fpdfapi/render/cpdf_rendercontext.h"
28  #include "core/fpdfapi/render/cpdf_renderoptions.h"
29  #include "core/fpdfdoc/cpdf_nametree.h"
30  #include "core/fpdfdoc/cpdf_viewerpreferences.h"
31  #include "core/fxcrt/cfx_readonlymemorystream.h"
32  #include "core/fxcrt/fx_stream.h"
33  #include "core/fxcrt/fx_system.h"
34  #include "core/fxcrt/unowned_ptr.h"
35  #include "core/fxge/cfx_defaultrenderdevice.h"
36  #include "core/fxge/cfx_gemodule.h"
37  #include "core/fxge/cfx_renderdevice.h"
38  #include "fpdfsdk/cpdfsdk_customaccess.h"
39  #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
40  #include "fpdfsdk/cpdfsdk_helpers.h"
41  #include "fpdfsdk/cpdfsdk_pageview.h"
42  #include "fpdfsdk/cpdfsdk_renderpage.h"
43  #include "fxjs/ijs_runtime.h"
44  #include "public/fpdf_formfill.h"
45  #include "third_party/base/ptr_util.h"
46  #include "third_party/base/span.h"
47  
48  #ifdef PDF_ENABLE_XFA
49  #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
50  #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
51  #include "fxbarcode/BC_Library.h"
52  #endif  // PDF_ENABLE_XFA
53  
54  #if defined(OS_WIN)
55  #include "core/fpdfapi/render/cpdf_progressiverenderer.h"
56  #include "core/fpdfapi/render/cpdf_windowsrenderdevice.h"
57  #include "public/fpdf_edit.h"
58  
59  // These checks are here because core/ and public/ cannot depend on each other.
60  static_assert(WindowsPrintMode::kModeEmf == FPDF_PRINTMODE_EMF,
61                "WindowsPrintMode::kModeEmf value mismatch");
62  static_assert(WindowsPrintMode::kModeTextOnly == FPDF_PRINTMODE_TEXTONLY,
63                "WindowsPrintMode::kModeTextOnly value mismatch");
64  static_assert(WindowsPrintMode::kModePostScript2 == FPDF_PRINTMODE_POSTSCRIPT2,
65                "WindowsPrintMode::kModePostScript2 value mismatch");
66  static_assert(WindowsPrintMode::kModePostScript3 == FPDF_PRINTMODE_POSTSCRIPT3,
67                "WindowsPrintMode::kModePostScript3 value mismatch");
68  static_assert(WindowsPrintMode::kModePostScript2PassThrough ==
69                    FPDF_PRINTMODE_POSTSCRIPT2_PASSTHROUGH,
70                "WindowsPrintMode::kModePostScript2PassThrough value mismatch");
71  static_assert(WindowsPrintMode::kModePostScript3PassThrough ==
72                    FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH,
73                "WindowsPrintMode::kModePostScript3PassThrough value mismatch");
74  #endif  // defined(OS_WIN)
75  
76  namespace {
77  
78  bool g_bLibraryInitialized = false;
79  
LoadDocumentImpl(const RetainPtr<IFX_SeekableReadStream> & pFileAccess,FPDF_BYTESTRING password)80  FPDF_DOCUMENT LoadDocumentImpl(
81      const RetainPtr<IFX_SeekableReadStream>& pFileAccess,
82      FPDF_BYTESTRING password) {
83    if (!pFileAccess) {
84      ProcessParseError(CPDF_Parser::FILE_ERROR);
85      return nullptr;
86    }
87  
88    auto pDocument = pdfium::MakeUnique<CPDF_Document>(
89        pdfium::MakeUnique<CPDF_DocRenderData>(),
90        pdfium::MakeUnique<CPDF_DocPageData>());
91  
92    CPDF_Parser::Error error = pDocument->LoadDoc(pFileAccess, password);
93    if (error != CPDF_Parser::SUCCESS) {
94      ProcessParseError(error);
95      return nullptr;
96    }
97  
98    ReportUnsupportedFeatures(pDocument.get());
99    return FPDFDocumentFromCPDFDocument(pDocument.release());
100  }
101  
102  }  // namespace
103  
FPDF_InitLibrary()104  FPDF_EXPORT void FPDF_CALLCONV FPDF_InitLibrary() {
105    FPDF_InitLibraryWithConfig(nullptr);
106  }
107  
108  FPDF_EXPORT void FPDF_CALLCONV
FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG * config)109  FPDF_InitLibraryWithConfig(const FPDF_LIBRARY_CONFIG* config) {
110    if (g_bLibraryInitialized)
111      return;
112  
113    FXMEM_InitializePartitionAlloc();
114    CFX_GEModule::Create(config ? config->m_pUserFontPaths : nullptr);
115    CPDF_PageModule::Create();
116  
117  #ifdef PDF_ENABLE_XFA
118    BC_Library_Init();
119  #endif  // PDF_ENABLE_XFA
120    if (config && config->version >= 2)
121      IJS_Runtime::Initialize(config->m_v8EmbedderSlot, config->m_pIsolate);
122  
123    g_bLibraryInitialized = true;
124  }
125  
FPDF_DestroyLibrary()126  FPDF_EXPORT void FPDF_CALLCONV FPDF_DestroyLibrary() {
127    if (!g_bLibraryInitialized)
128      return;
129  
130  #ifdef PDF_ENABLE_XFA
131    BC_Library_Destroy();
132  #endif  // PDF_ENABLE_XFA
133  
134    CPDF_PageModule::Destroy();
135    CFX_GEModule::Destroy();
136    IJS_Runtime::Destroy();
137  
138    g_bLibraryInitialized = false;
139  }
140  
FPDF_SetSandBoxPolicy(FPDF_DWORD policy,FPDF_BOOL enable)141  FPDF_EXPORT void FPDF_CALLCONV FPDF_SetSandBoxPolicy(FPDF_DWORD policy,
142                                                       FPDF_BOOL enable) {
143    return SetPDFSandboxPolicy(policy, enable);
144  }
145  
146  #if defined(OS_WIN)
147  #if defined(PDFIUM_PRINT_TEXT_WITH_GDI)
148  FPDF_EXPORT void FPDF_CALLCONV
FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func)149  FPDF_SetTypefaceAccessibleFunc(PDFiumEnsureTypefaceCharactersAccessible func) {
150    g_pdfium_typeface_accessible_func = func;
151  }
152  
FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi)153  FPDF_EXPORT void FPDF_CALLCONV FPDF_SetPrintTextWithGDI(FPDF_BOOL use_gdi) {
154    g_pdfium_print_text_with_gdi = !!use_gdi;
155  }
156  #endif  // PDFIUM_PRINT_TEXT_WITH_GDI
157  
FPDF_SetPrintMode(int mode)158  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_SetPrintMode(int mode) {
159    if (mode < FPDF_PRINTMODE_EMF ||
160        mode > FPDF_PRINTMODE_POSTSCRIPT3_PASSTHROUGH) {
161      return FALSE;
162    }
163    g_pdfium_print_mode = static_cast<WindowsPrintMode>(mode);
164    return TRUE;
165  }
166  #endif  // defined(OS_WIN)
167  
168  FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_LoadDocument(FPDF_STRING file_path,FPDF_BYTESTRING password)169  FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password) {
170    // NOTE: the creation of the file needs to be by the embedder on the
171    // other side of this API.
172    return LoadDocumentImpl(IFX_SeekableReadStream::CreateFromFilename(file_path),
173                            password);
174  }
175  
FPDF_GetFormType(FPDF_DOCUMENT document)176  FPDF_EXPORT int FPDF_CALLCONV FPDF_GetFormType(FPDF_DOCUMENT document) {
177    const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
178    if (!pDoc)
179      return FORMTYPE_NONE;
180  
181    const CPDF_Dictionary* pRoot = pDoc->GetRoot();
182    if (!pRoot)
183      return FORMTYPE_NONE;
184  
185    const CPDF_Dictionary* pAcroForm = pRoot->GetDictFor("AcroForm");
186    if (!pAcroForm)
187      return FORMTYPE_NONE;
188  
189    const CPDF_Object* pXFA = pAcroForm->GetObjectFor("XFA");
190    if (!pXFA)
191      return FORMTYPE_ACRO_FORM;
192  
193    bool bNeedsRendering = pRoot->GetBooleanFor("NeedsRendering", false);
194    return bNeedsRendering ? FORMTYPE_XFA_FULL : FORMTYPE_XFA_FOREGROUND;
195  }
196  
FPDF_LoadXFA(FPDF_DOCUMENT document)197  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_LoadXFA(FPDF_DOCUMENT document) {
198  #ifdef PDF_ENABLE_XFA
199    auto* pDoc = CPDFDocumentFromFPDFDocument(document);
200    if (!pDoc)
201      return false;
202  
203    auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
204    if (pContext)
205      return pContext->LoadXFADoc();
206  #endif  // PDF_ENABLE_XFA
207    return false;
208  }
209  
210  FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_LoadMemDocument(const void * data_buf,int size,FPDF_BYTESTRING password)211  FPDF_LoadMemDocument(const void* data_buf, int size, FPDF_BYTESTRING password) {
212    return LoadDocumentImpl(
213        pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
214            pdfium::make_span(static_cast<const uint8_t*>(data_buf), size)),
215        password);
216  }
217  
218  FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
FPDF_LoadCustomDocument(FPDF_FILEACCESS * pFileAccess,FPDF_BYTESTRING password)219  FPDF_LoadCustomDocument(FPDF_FILEACCESS* pFileAccess,
220                          FPDF_BYTESTRING password) {
221    if (!pFileAccess)
222      return nullptr;
223    return LoadDocumentImpl(pdfium::MakeRetain<CPDFSDK_CustomAccess>(pFileAccess),
224                            password);
225  }
226  
FPDF_GetFileVersion(FPDF_DOCUMENT doc,int * fileVersion)227  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetFileVersion(FPDF_DOCUMENT doc,
228                                                          int* fileVersion) {
229    if (!fileVersion)
230      return false;
231  
232    *fileVersion = 0;
233    CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(doc);
234    if (!pDoc)
235      return false;
236  
237    const CPDF_Parser* pParser = pDoc->GetParser();
238    if (!pParser)
239      return false;
240  
241    *fileVersion = pParser->GetFileVersion();
242    return true;
243  }
244  
245  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_DocumentHasValidCrossReferenceTable(FPDF_DOCUMENT document)246  FPDF_DocumentHasValidCrossReferenceTable(FPDF_DOCUMENT document) {
247    CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
248    return pDoc && pDoc->has_valid_cross_reference_table();
249  }
250  
251  FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetDocPermissions(FPDF_DOCUMENT document)252  FPDF_GetDocPermissions(FPDF_DOCUMENT document) {
253    CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
254    return pDoc ? pDoc->GetUserPermissions() : 0;
255  }
256  
257  FPDF_EXPORT int FPDF_CALLCONV
FPDF_GetSecurityHandlerRevision(FPDF_DOCUMENT document)258  FPDF_GetSecurityHandlerRevision(FPDF_DOCUMENT document) {
259    CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
260    if (!pDoc || !pDoc->GetParser())
261      return -1;
262  
263    const CPDF_Dictionary* pDict = pDoc->GetParser()->GetEncryptDict();
264    return pDict ? pDict->GetIntegerFor("R") : -1;
265  }
266  
FPDF_GetPageCount(FPDF_DOCUMENT document)267  FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageCount(FPDF_DOCUMENT document) {
268    auto* pDoc = CPDFDocumentFromFPDFDocument(document);
269    if (!pDoc)
270      return 0;
271  
272    auto* pExtension = pDoc->GetExtension();
273    return pExtension ? pExtension->GetPageCount() : pDoc->GetPageCount();
274  }
275  
FPDF_LoadPage(FPDF_DOCUMENT document,int page_index)276  FPDF_EXPORT FPDF_PAGE FPDF_CALLCONV FPDF_LoadPage(FPDF_DOCUMENT document,
277                                                    int page_index) {
278    auto* pDoc = CPDFDocumentFromFPDFDocument(document);
279    if (!pDoc)
280      return nullptr;
281  
282    if (page_index < 0 || page_index >= FPDF_GetPageCount(document))
283      return nullptr;
284  
285  #ifdef PDF_ENABLE_XFA
286    auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
287    if (pContext)
288      return FPDFPageFromIPDFPage(pContext->GetXFAPage(page_index).Leak());
289  #endif  // PDF_ENABLE_XFA
290  
291    CPDF_Dictionary* pDict = pDoc->GetPageDictionary(page_index);
292    if (!pDict)
293      return nullptr;
294  
295    auto pPage = pdfium::MakeRetain<CPDF_Page>(pDoc, pDict);
296    pPage->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(pPage.Get()));
297    pPage->ParseContent();
298    return FPDFPageFromIPDFPage(pPage.Leak());
299  }
300  
FPDF_GetPageWidthF(FPDF_PAGE page)301  FPDF_EXPORT float FPDF_CALLCONV FPDF_GetPageWidthF(FPDF_PAGE page) {
302    IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
303    return pPage ? pPage->GetPageWidth() : 0.0f;
304  }
305  
FPDF_GetPageWidth(FPDF_PAGE page)306  FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageWidth(FPDF_PAGE page) {
307    return FPDF_GetPageWidthF(page);
308  }
309  
FPDF_GetPageHeightF(FPDF_PAGE page)310  FPDF_EXPORT float FPDF_CALLCONV FPDF_GetPageHeightF(FPDF_PAGE page) {
311    IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
312    return pPage ? pPage->GetPageHeight() : 0.0f;
313  }
314  
FPDF_GetPageHeight(FPDF_PAGE page)315  FPDF_EXPORT double FPDF_CALLCONV FPDF_GetPageHeight(FPDF_PAGE page) {
316    return FPDF_GetPageHeightF(page);
317  }
318  
FPDF_GetPageBoundingBox(FPDF_PAGE page,FS_RECTF * rect)319  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_GetPageBoundingBox(FPDF_PAGE page,
320                                                              FS_RECTF* rect) {
321    if (!rect)
322      return false;
323  
324    CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
325    if (!pPage)
326      return false;
327  
328    *rect = FSRectFFromCFXFloatRect(pPage->GetBBox());
329    return true;
330  }
331  
332  #if defined(OS_WIN)
333  namespace {
334  
335  const double kEpsilonSize = 0.01f;
336  
GetScaling(CPDF_Page * pPage,int size_x,int size_y,int rotate,double * scale_x,double * scale_y)337  void GetScaling(CPDF_Page* pPage,
338                  int size_x,
339                  int size_y,
340                  int rotate,
341                  double* scale_x,
342                  double* scale_y) {
343    ASSERT(pPage);
344    ASSERT(scale_x);
345    ASSERT(scale_y);
346    double page_width = pPage->GetPageWidth();
347    double page_height = pPage->GetPageHeight();
348    if (page_width < kEpsilonSize || page_height < kEpsilonSize)
349      return;
350  
351    if (rotate % 2 == 0) {
352      *scale_x = size_x / page_width;
353      *scale_y = size_y / page_height;
354    } else {
355      *scale_x = size_y / page_width;
356      *scale_y = size_x / page_height;
357    }
358  }
359  
GetMaskDimensionsAndOffsets(CPDF_Page * pPage,int start_x,int start_y,int size_x,int size_y,int rotate,const CFX_FloatRect & mask_box)360  FX_RECT GetMaskDimensionsAndOffsets(CPDF_Page* pPage,
361                                      int start_x,
362                                      int start_y,
363                                      int size_x,
364                                      int size_y,
365                                      int rotate,
366                                      const CFX_FloatRect& mask_box) {
367    double scale_x = 0.0f;
368    double scale_y = 0.0f;
369    GetScaling(pPage, size_x, size_y, rotate, &scale_x, &scale_y);
370    if (scale_x < kEpsilonSize || scale_y < kEpsilonSize)
371      return FX_RECT();
372  
373    // Compute sizes in page points. Round down to catch the entire bitmap.
374    int start_x_bm = static_cast<int>(mask_box.left * scale_x);
375    int start_y_bm = static_cast<int>(mask_box.bottom * scale_y);
376    int size_x_bm = static_cast<int>(mask_box.right * scale_x + 1.0f) -
377                    static_cast<int>(mask_box.left * scale_x);
378    int size_y_bm = static_cast<int>(mask_box.top * scale_y + 1.0f) -
379                    static_cast<int>(mask_box.bottom * scale_y);
380  
381    // Get page rotation
382    int page_rotation = pPage->GetPageRotation();
383  
384    // Compute offsets
385    int offset_x = 0;
386    int offset_y = 0;
387    if (size_x > size_y)
388      std::swap(size_x_bm, size_y_bm);
389  
390    switch ((rotate + page_rotation) % 4) {
391      case 0:
392        offset_x = start_x_bm + start_x;
393        offset_y = start_y + size_y - size_y_bm - start_y_bm;
394        break;
395      case 1:
396        offset_x = start_y_bm + start_x;
397        offset_y = start_x_bm + start_y;
398        break;
399      case 2:
400        offset_x = start_x + size_x - size_x_bm - start_x_bm;
401        offset_y = start_y_bm + start_y;
402        break;
403      case 3:
404        offset_x = start_x + size_x - size_x_bm - start_y_bm;
405        offset_y = start_y + size_y - size_y_bm - start_x_bm;
406        break;
407    }
408    return FX_RECT(offset_x, offset_y, offset_x + size_x_bm,
409                   offset_y + size_y_bm);
410  }
411  
412  // Get a bitmap of just the mask section defined by |mask_box| from a full page
413  // 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)414  RetainPtr<CFX_DIBitmap> GetMaskBitmap(CPDF_Page* pPage,
415                                        int start_x,
416                                        int start_y,
417                                        int size_x,
418                                        int size_y,
419                                        int rotate,
420                                        const RetainPtr<CFX_DIBitmap>& pSrc,
421                                        const CFX_FloatRect& mask_box,
422                                        FX_RECT* bitmap_area) {
423    ASSERT(bitmap_area);
424    *bitmap_area = GetMaskDimensionsAndOffsets(pPage, start_x, start_y, size_x,
425                                               size_y, rotate, mask_box);
426    if (bitmap_area->IsEmpty())
427      return nullptr;
428  
429    // Create a new bitmap to transfer part of the page bitmap to.
430    RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
431    if (!pDst->Create(bitmap_area->Width(), bitmap_area->Height(), FXDIB_Argb))
432      return nullptr;
433  
434    pDst->Clear(0x00ffffff);
435    pDst->TransferBitmap(0, 0, bitmap_area->Width(), bitmap_area->Height(), pSrc,
436                         bitmap_area->left, bitmap_area->top);
437    return pDst;
438  }
439  
RenderBitmap(CFX_RenderDevice * device,const RetainPtr<CFX_DIBitmap> & pSrc,const FX_RECT & mask_area)440  void RenderBitmap(CFX_RenderDevice* device,
441                    const RetainPtr<CFX_DIBitmap>& pSrc,
442                    const FX_RECT& mask_area) {
443    int size_x_bm = mask_area.Width();
444    int size_y_bm = mask_area.Height();
445    if (size_x_bm == 0 || size_y_bm == 0)
446      return;
447  
448    // Create a new bitmap from the old one
449    RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>();
450    if (!pDst->Create(size_x_bm, size_y_bm, FXDIB_Rgb32))
451      return;
452  
453    pDst->Clear(0xffffffff);
454    pDst->CompositeBitmap(0, 0, size_x_bm, size_y_bm, pSrc, 0, 0,
455                          BlendMode::kNormal, nullptr, false);
456  
457    if (device->GetDeviceType() == DeviceType::kPrinter) {
458      device->StretchDIBits(pDst, mask_area.left, mask_area.top, size_x_bm,
459                            size_y_bm);
460    } else {
461      device->SetDIBits(pDst, mask_area.left, mask_area.top);
462    }
463  }
464  
465  }  // namespace
466  
FPDF_RenderPage(HDC dc,FPDF_PAGE page,int start_x,int start_y,int size_x,int size_y,int rotate,int flags)467  FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPage(HDC dc,
468                                                 FPDF_PAGE page,
469                                                 int start_x,
470                                                 int start_y,
471                                                 int size_x,
472                                                 int size_y,
473                                                 int rotate,
474                                                 int flags) {
475    CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
476    if (!pPage)
477      return;
478  
479    auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
480    CPDF_PageRenderContext* pContext = pOwnedContext.get();
481    CPDF_Page::RenderContextClearer clearer(pPage);
482    pPage->SetRenderContext(std::move(pOwnedContext));
483  
484    // Don't render the full page to bitmap for a mask unless there are a lot
485    // of masks. Full page bitmaps result in large spool sizes, so they should
486    // only be used when necessary. For large numbers of masks, rendering each
487    // individually is inefficient and unlikely to significantly improve spool
488    // size. TODO(rbpotter): Find out why this still breaks printing for some
489    // PDFs (see crbug.com/777837).
490    const bool bEnableImageMasks = false;
491    const bool bNewBitmap = pPage->BackgroundAlphaNeeded() ||
492                            (pPage->HasImageMask() && !bEnableImageMasks) ||
493                            pPage->GetMaskBoundingBoxes().size() > 100;
494    const bool bHasMask = pPage->HasImageMask() && !bNewBitmap;
495    if (!bNewBitmap && !bHasMask) {
496      pContext->m_pDevice = pdfium::MakeUnique<CPDF_WindowsRenderDevice>(dc);
497      CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
498                                    size_y, rotate, flags,
499                                    /*need_to_restore=*/true, /*pause=*/nullptr);
500      return;
501    }
502  
503    RetainPtr<CFX_DIBitmap> pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
504    // Create will probably work fine even if it fails here: we will just attach
505    // a zero-sized bitmap to |pDevice|.
506    pBitmap->Create(size_x, size_y, FXDIB_Argb);
507    pBitmap->Clear(0x00ffffff);
508    CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice;
509    pContext->m_pDevice = pdfium::WrapUnique(pDevice);
510    pDevice->Attach(pBitmap, false, nullptr, false);
511    if (bHasMask) {
512      pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
513      pContext->m_pOptions->GetOptions().bBreakForMasks = true;
514    }
515  
516    CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
517                                  size_y, rotate, flags, /*need_to_restore=*/true,
518                                  /*pause=*/nullptr);
519  
520    if (!bHasMask) {
521      CPDF_WindowsRenderDevice WinDC(dc);
522      bool bitsStretched = false;
523      if (WinDC.GetDeviceType() == DeviceType::kPrinter) {
524        auto pDst = pdfium::MakeRetain<CFX_DIBitmap>();
525        if (pDst->Create(size_x, size_y, FXDIB_Rgb32)) {
526          memset(pDst->GetBuffer(), -1, pBitmap->GetPitch() * size_y);
527          pDst->CompositeBitmap(0, 0, size_x, size_y, pBitmap, 0, 0,
528                                BlendMode::kNormal, nullptr, false);
529          WinDC.StretchDIBits(pDst, 0, 0, size_x, size_y);
530          bitsStretched = true;
531        }
532      }
533      if (!bitsStretched)
534        WinDC.SetDIBits(pBitmap, 0, 0);
535      return;
536    }
537  
538    // Finish rendering the page to bitmap and copy the correct segments
539    // of the page to individual image mask bitmaps.
540    const std::vector<CFX_FloatRect>& mask_boxes = pPage->GetMaskBoundingBoxes();
541    std::vector<FX_RECT> bitmap_areas(mask_boxes.size());
542    std::vector<RetainPtr<CFX_DIBitmap>> bitmaps(mask_boxes.size());
543    for (size_t i = 0; i < mask_boxes.size(); i++) {
544      bitmaps[i] = GetMaskBitmap(pPage, start_x, start_y, size_x, size_y, rotate,
545                                 pBitmap, mask_boxes[i], &bitmap_areas[i]);
546      pContext->m_pRenderer->Continue(nullptr);
547    }
548  
549    // Begin rendering to the printer. Add flag to indicate the renderer should
550    // pause after each image mask.
551    pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
552    pContext = pOwnedContext.get();
553    pPage->SetRenderContext(std::move(pOwnedContext));
554    pContext->m_pDevice = pdfium::MakeUnique<CPDF_WindowsRenderDevice>(dc);
555    pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>();
556    pContext->m_pOptions->GetOptions().bBreakForMasks = true;
557  
558    CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
559                                  size_y, rotate, flags, /*need_to_restore=*/true,
560                                  /*pause=*/nullptr);
561  
562    // Render masks
563    for (size_t i = 0; i < mask_boxes.size(); i++) {
564      // Render the bitmap for the mask and free the bitmap.
565      if (bitmaps[i]) {  // will be null if mask has zero area
566        RenderBitmap(pContext->m_pDevice.get(), bitmaps[i], bitmap_areas[i]);
567      }
568      // Render the next portion of page.
569      pContext->m_pRenderer->Continue(nullptr);
570    }
571  }
572  #endif  // defined(OS_WIN)
573  
FPDF_RenderPageBitmap(FPDF_BITMAP bitmap,FPDF_PAGE page,int start_x,int start_y,int size_x,int size_y,int rotate,int flags)574  FPDF_EXPORT void FPDF_CALLCONV FPDF_RenderPageBitmap(FPDF_BITMAP bitmap,
575                                                       FPDF_PAGE page,
576                                                       int start_x,
577                                                       int start_y,
578                                                       int size_x,
579                                                       int size_y,
580                                                       int rotate,
581                                                       int flags) {
582    if (!bitmap)
583      return;
584  
585    CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
586    if (!pPage)
587      return;
588  
589    auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
590    CPDF_PageRenderContext* pContext = pOwnedContext.get();
591    CPDF_Page::RenderContextClearer clearer(pPage);
592    pPage->SetRenderContext(std::move(pOwnedContext));
593  
594    auto pOwnedDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
595    CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
596    pContext->m_pDevice = std::move(pOwnedDevice);
597  
598    RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
599    pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
600    CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
601                                  size_y, rotate, flags, /*need_to_restore=*/true,
602                                  /*pause=*/nullptr);
603  
604  #ifdef _SKIA_SUPPORT_PATHS_
605    pDevice->Flush(true);
606    pBitmap->UnPreMultiply();
607  #endif
608  }
609  
610  FPDF_EXPORT void FPDF_CALLCONV
FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap,FPDF_PAGE page,const FS_MATRIX * matrix,const FS_RECTF * clipping,int flags)611  FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap,
612                                  FPDF_PAGE page,
613                                  const FS_MATRIX* matrix,
614                                  const FS_RECTF* clipping,
615                                  int flags) {
616    if (!bitmap)
617      return;
618  
619    CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
620    if (!pPage)
621      return;
622  
623    auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
624    CPDF_PageRenderContext* pContext = pOwnedContext.get();
625    CPDF_Page::RenderContextClearer clearer(pPage);
626    pPage->SetRenderContext(std::move(pOwnedContext));
627  
628    auto pOwnedDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
629    CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
630    pContext->m_pDevice = std::move(pOwnedDevice);
631  
632    RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
633    pDevice->Attach(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER), nullptr, false);
634  
635    CFX_FloatRect clipping_rect;
636    if (clipping)
637      clipping_rect = CFXFloatRectFromFSRectF(*clipping);
638    FX_RECT clip_rect = clipping_rect.ToFxRect();
639  
640    const FX_RECT rect(0, 0, pPage->GetPageWidth(), pPage->GetPageHeight());
641    CFX_Matrix transform_matrix = pPage->GetDisplayMatrix(rect, 0);
642    if (matrix)
643      transform_matrix *= CFXMatrixFromFSMatrix(*matrix);
644    CPDFSDK_RenderPage(pContext, pPage, transform_matrix, clip_rect, flags);
645  }
646  
647  #ifdef _SKIA_SUPPORT_
FPDF_RenderPageSkp(FPDF_PAGE page,int size_x,int size_y)648  FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page,
649                                                             int size_x,
650                                                             int size_y) {
651    CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
652    if (!pPage)
653      return nullptr;
654  
655    auto pOwnedContext = pdfium::MakeUnique<CPDF_PageRenderContext>();
656    CPDF_PageRenderContext* pContext = pOwnedContext.get();
657    CPDF_Page::RenderContextClearer clearer(pPage);
658    pPage->SetRenderContext(std::move(pOwnedContext));
659  
660    auto skDevice = pdfium::MakeUnique<CFX_DefaultRenderDevice>();
661    FPDF_RECORDER recorder = skDevice->CreateRecorder(size_x, size_y);
662    pContext->m_pDevice = std::move(skDevice);
663  
664    CPDFSDK_RenderPageWithContext(pContext, pPage, 0, 0, size_x, size_y, 0, 0,
665                                  /*need_to_restore=*/true, /*pause=*/nullptr);
666    return recorder;
667  }
668  #endif  // _SKIA_SUPPORT_
669  
FPDF_ClosePage(FPDF_PAGE page)670  FPDF_EXPORT void FPDF_CALLCONV FPDF_ClosePage(FPDF_PAGE page) {
671    if (!page)
672      return;
673  
674    // Take it back across the API and hold for duration of this function.
675    RetainPtr<IPDF_Page> pPage;
676    pPage.Unleak(IPDFPageFromFPDFPage(page));
677  
678    if (pPage->AsXFAPage())
679      return;
680  
681    CPDFSDK_PageView* pPageView =
682        static_cast<CPDFSDK_PageView*>(pPage->AsPDFPage()->GetView());
683    if (!pPageView || pPageView->IsBeingDestroyed())
684      return;
685  
686    if (pPageView->IsLocked()) {
687      pPageView->TakePageOwnership();
688      return;
689    }
690  
691    // This will delete the |pPageView| object. We must cleanup the PageView
692    // first because it will attempt to reset the View on the |pPage| during
693    // destruction.
694    pPageView->GetFormFillEnv()->RemovePageView(pPage.Get());
695  }
696  
FPDF_CloseDocument(FPDF_DOCUMENT document)697  FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseDocument(FPDF_DOCUMENT document) {
698    // Take it back across the API and throw it away,
699    std::unique_ptr<CPDF_Document>(CPDFDocumentFromFPDFDocument(document));
700  }
701  
FPDF_GetLastError()702  FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError() {
703    return FXSYS_GetLastError();
704  }
705  
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)706  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_DeviceToPage(FPDF_PAGE page,
707                                                        int start_x,
708                                                        int start_y,
709                                                        int size_x,
710                                                        int size_y,
711                                                        int rotate,
712                                                        int device_x,
713                                                        int device_y,
714                                                        double* page_x,
715                                                        double* page_y) {
716    if (!page || !page_x || !page_y)
717      return false;
718  
719    IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
720    const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
721    Optional<CFX_PointF> pos =
722        pPage->DeviceToPage(rect, rotate, CFX_PointF(device_x, device_y));
723    if (!pos)
724      return false;
725  
726    *page_x = pos->x;
727    *page_y = pos->y;
728    return true;
729  }
730  
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)731  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_PageToDevice(FPDF_PAGE page,
732                                                        int start_x,
733                                                        int start_y,
734                                                        int size_x,
735                                                        int size_y,
736                                                        int rotate,
737                                                        double page_x,
738                                                        double page_y,
739                                                        int* device_x,
740                                                        int* device_y) {
741    if (!page || !device_x || !device_y)
742      return false;
743  
744    IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
745    const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
746    CFX_PointF page_point(static_cast<float>(page_x), static_cast<float>(page_y));
747    Optional<CFX_PointF> pos = pPage->PageToDevice(rect, rotate, page_point);
748    if (!pos)
749      return false;
750  
751    *device_x = FXSYS_roundf(pos->x);
752    *device_y = FXSYS_roundf(pos->y);
753    return true;
754  }
755  
FPDFBitmap_Create(int width,int height,int alpha)756  FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width,
757                                                          int height,
758                                                          int alpha) {
759    auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
760    if (!pBitmap->Create(width, height, alpha ? FXDIB_Argb : FXDIB_Rgb32))
761      return nullptr;
762  
763    return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
764  }
765  
FPDFBitmap_CreateEx(int width,int height,int format,void * first_scan,int stride)766  FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width,
767                                                            int height,
768                                                            int format,
769                                                            void* first_scan,
770                                                            int stride) {
771    FXDIB_Format fx_format;
772    switch (format) {
773      case FPDFBitmap_Gray:
774        fx_format = FXDIB_8bppRgb;
775        break;
776      case FPDFBitmap_BGR:
777        fx_format = FXDIB_Rgb;
778        break;
779      case FPDFBitmap_BGRx:
780        fx_format = FXDIB_Rgb32;
781        break;
782      case FPDFBitmap_BGRA:
783        fx_format = FXDIB_Argb;
784        break;
785      default:
786        return nullptr;
787    }
788  
789    // Ensure external memory is good at least for the duration of this call.
790    UnownedPtr<uint8_t> pChecker(static_cast<uint8_t*>(first_scan));
791    auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
792    if (!pBitmap->Create(width, height, fx_format, pChecker.Get(), stride))
793      return nullptr;
794  
795    return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
796  }
797  
FPDFBitmap_GetFormat(FPDF_BITMAP bitmap)798  FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetFormat(FPDF_BITMAP bitmap) {
799    if (!bitmap)
800      return FPDFBitmap_Unknown;
801  
802    FXDIB_Format format = CFXDIBitmapFromFPDFBitmap(bitmap)->GetFormat();
803    switch (format) {
804      case FXDIB_8bppRgb:
805      case FXDIB_8bppMask:
806        return FPDFBitmap_Gray;
807      case FXDIB_Rgb:
808        return FPDFBitmap_BGR;
809      case FXDIB_Rgb32:
810        return FPDFBitmap_BGRx;
811      case FXDIB_Argb:
812        return FPDFBitmap_BGRA;
813      default:
814        return FPDFBitmap_Unknown;
815    }
816  }
817  
FPDFBitmap_FillRect(FPDF_BITMAP bitmap,int left,int top,int width,int height,FPDF_DWORD color)818  FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_FillRect(FPDF_BITMAP bitmap,
819                                                     int left,
820                                                     int top,
821                                                     int width,
822                                                     int height,
823                                                     FPDF_DWORD color) {
824    if (!bitmap)
825      return;
826  
827    CFX_DefaultRenderDevice device;
828    RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
829    device.Attach(pBitmap, false, nullptr, false);
830    if (!pBitmap->HasAlpha())
831      color |= 0xFF000000;
832    device.FillRect(FX_RECT(left, top, left + width, top + height), color);
833  }
834  
FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap)835  FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {
836    return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetBuffer() : nullptr;
837  }
838  
FPDFBitmap_GetWidth(FPDF_BITMAP bitmap)839  FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetWidth(FPDF_BITMAP bitmap) {
840    return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetWidth() : 0;
841  }
842  
FPDFBitmap_GetHeight(FPDF_BITMAP bitmap)843  FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetHeight(FPDF_BITMAP bitmap) {
844    return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetHeight() : 0;
845  }
846  
FPDFBitmap_GetStride(FPDF_BITMAP bitmap)847  FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetStride(FPDF_BITMAP bitmap) {
848    return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetPitch() : 0;
849  }
850  
FPDFBitmap_Destroy(FPDF_BITMAP bitmap)851  FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_Destroy(FPDF_BITMAP bitmap) {
852    RetainPtr<CFX_DIBitmap> destroyer;
853    destroyer.Unleak(CFXDIBitmapFromFPDFBitmap(bitmap));
854  }
855  
856  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document,int page_index,FS_SIZEF * size)857  FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document,
858                           int page_index,
859                           FS_SIZEF* size) {
860    if (!size)
861      return false;
862  
863    auto* pDoc = CPDFDocumentFromFPDFDocument(document);
864    if (!pDoc)
865      return false;
866  
867  #ifdef PDF_ENABLE_XFA
868    if (page_index < 0 || page_index >= FPDF_GetPageCount(document))
869      return false;
870  
871    auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
872    if (pContext) {
873      RetainPtr<CPDFXFA_Page> pPage = pContext->GetXFAPage(page_index);
874      if (!pPage)
875        return false;
876  
877      size->width = pPage->GetPageWidth();
878      size->height = pPage->GetPageHeight();
879      return true;
880    }
881  #endif  // PDF_ENABLE_XFA
882  
883    CPDF_Dictionary* pDict = pDoc->GetPageDictionary(page_index);
884    if (!pDict)
885      return false;
886  
887    auto page = pdfium::MakeRetain<CPDF_Page>(pDoc, pDict);
888    page->SetRenderCache(pdfium::MakeUnique<CPDF_PageRenderCache>(page.Get()));
889    size->width = page->GetPageWidth();
890    size->height = page->GetPageHeight();
891    return true;
892  }
893  
FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,int page_index,double * width,double * height)894  FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,
895                                                        int page_index,
896                                                        double* width,
897                                                        double* height) {
898    if (!width || !height)
899      return false;
900  
901    FS_SIZEF size;
902    if (!FPDF_GetPageSizeByIndexF(document, page_index, &size))
903      return false;
904  
905    *width = size.width;
906    *height = size.height;
907    return true;
908  }
909  
910  FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document)911  FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document) {
912    const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
913    if (!pDoc)
914      return true;
915    CPDF_ViewerPreferences viewRef(pDoc);
916    return viewRef.PrintScaling();
917  }
918  
919  FPDF_EXPORT int FPDF_CALLCONV
FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document)920  FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document) {
921    const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
922    if (!pDoc)
923      return 1;
924    CPDF_ViewerPreferences viewRef(pDoc);
925    return viewRef.NumCopies();
926  }
927  
928  FPDF_EXPORT FPDF_PAGERANGE FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document)929  FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document) {
930    const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
931    if (!pDoc)
932      return nullptr;
933    CPDF_ViewerPreferences viewRef(pDoc);
934    return FPDFPageRangeFromCPDFArray(viewRef.PrintPageRange());
935  }
936  
937  FPDF_EXPORT size_t FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange)938  FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange) {
939    const CPDF_Array* pArray = CPDFArrayFromFPDFPageRange(pagerange);
940    return pArray ? pArray->size() : 0;
941  }
942  
943  FPDF_EXPORT int FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRangeElement(FPDF_PAGERANGE pagerange,size_t index)944  FPDF_VIEWERREF_GetPrintPageRangeElement(FPDF_PAGERANGE pagerange,
945                                          size_t index) {
946    const CPDF_Array* pArray = CPDFArrayFromFPDFPageRange(pagerange);
947    if (!pArray || index >= pArray->size())
948      return -1;
949    return pArray->GetIntegerAt(index);
950  }
951  
952  FPDF_EXPORT FPDF_DUPLEXTYPE FPDF_CALLCONV
FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document)953  FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document) {
954    const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
955    if (!pDoc)
956      return DuplexUndefined;
957    CPDF_ViewerPreferences viewRef(pDoc);
958    ByteString duplex = viewRef.Duplex();
959    if ("Simplex" == duplex)
960      return Simplex;
961    if ("DuplexFlipShortEdge" == duplex)
962      return DuplexFlipShortEdge;
963    if ("DuplexFlipLongEdge" == duplex)
964      return DuplexFlipLongEdge;
965    return DuplexUndefined;
966  }
967  
968  FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,FPDF_BYTESTRING key,char * buffer,unsigned long length)969  FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,
970                         FPDF_BYTESTRING key,
971                         char* buffer,
972                         unsigned long length) {
973    const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
974    if (!pDoc)
975      return 0;
976  
977    CPDF_ViewerPreferences viewRef(pDoc);
978    Optional<ByteString> bsVal = viewRef.GenericName(key);
979    if (!bsVal)
980      return 0;
981  
982    unsigned long dwStringLen = bsVal->GetLength() + 1;
983    if (buffer && length >= dwStringLen)
984      memcpy(buffer, bsVal->c_str(), dwStringLen);
985    return dwStringLen;
986  }
987  
988  FPDF_EXPORT FPDF_DWORD FPDF_CALLCONV
FPDF_CountNamedDests(FPDF_DOCUMENT document)989  FPDF_CountNamedDests(FPDF_DOCUMENT document) {
990    CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
991    if (!pDoc)
992      return 0;
993  
994    const CPDF_Dictionary* pRoot = pDoc->GetRoot();
995    if (!pRoot)
996      return 0;
997  
998    CPDF_NameTree nameTree(pDoc, "Dests");
999    pdfium::base::CheckedNumeric<FPDF_DWORD> count = nameTree.GetCount();
1000    const CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests");
1001    if (pDest)
1002      count += pDest->size();
1003  
1004    if (!count.IsValid())
1005      return 0;
1006  
1007    return count.ValueOrDie();
1008  }
1009  
1010  FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
FPDF_GetNamedDestByName(FPDF_DOCUMENT document,FPDF_BYTESTRING name)1011  FPDF_GetNamedDestByName(FPDF_DOCUMENT document, FPDF_BYTESTRING name) {
1012    if (!name || name[0] == 0)
1013      return nullptr;
1014  
1015    CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1016    if (!pDoc)
1017      return nullptr;
1018  
1019    CPDF_NameTree name_tree(pDoc, "Dests");
1020    ByteStringView name_view(name);
1021    return FPDFDestFromCPDFArray(
1022        name_tree.LookupNamedDest(pDoc, PDF_DecodeText(name_view.raw_span())));
1023  }
1024  
1025  #ifdef PDF_ENABLE_V8
FPDF_GetRecommendedV8Flags()1026  FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags() {
1027    // Reduce exposure since no PDF should contain web assembly.
1028    // Use interpreted JS only to avoid RWX pages in our address space.
1029    return "--no-expose-wasm --jitless";
1030  }
1031  #endif  // PDF_ENABLE_V8
1032  
1033  #ifdef PDF_ENABLE_XFA
FPDF_BStr_Init(FPDF_BSTR * bstr)1034  FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* bstr) {
1035    if (!bstr)
1036      return -1;
1037  
1038    bstr->str = nullptr;
1039    bstr->len = 0;
1040    return 0;
1041  }
1042  
FPDF_BStr_Set(FPDF_BSTR * bstr,const char * cstr,int length)1043  FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* bstr,
1044                                                      const char* cstr,
1045                                                      int length) {
1046    if (!bstr || !cstr)
1047      return -1;
1048  
1049    if (length == -1)
1050      length = strlen(cstr);
1051  
1052    if (length == 0) {
1053      FPDF_BStr_Clear(bstr);
1054      return 0;
1055    }
1056  
1057    if (bstr->str && bstr->len < length)
1058      bstr->str = FX_Realloc(char, bstr->str, length + 1);
1059    else if (!bstr->str)
1060      bstr->str = FX_Alloc(char, length + 1);
1061  
1062    bstr->str[length] = 0;
1063    memcpy(bstr->str, cstr, length);
1064    bstr->len = length;
1065    return 0;
1066  }
1067  
FPDF_BStr_Clear(FPDF_BSTR * bstr)1068  FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* bstr) {
1069    if (!bstr)
1070      return -1;
1071  
1072    if (bstr->str) {
1073      FX_Free(bstr->str);
1074      bstr->str = nullptr;
1075    }
1076    bstr->len = 0;
1077    return 0;
1078  }
1079  #endif  // PDF_ENABLE_XFA
1080  
FPDF_GetNamedDest(FPDF_DOCUMENT document,int index,void * buffer,long * buflen)1081  FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document,
1082                                                        int index,
1083                                                        void* buffer,
1084                                                        long* buflen) {
1085    if (!buffer)
1086      *buflen = 0;
1087  
1088    if (index < 0)
1089      return nullptr;
1090  
1091    CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1092    if (!pDoc)
1093      return nullptr;
1094  
1095    const CPDF_Dictionary* pRoot = pDoc->GetRoot();
1096    if (!pRoot)
1097      return nullptr;
1098  
1099    CPDF_Object* pDestObj = nullptr;
1100    WideString wsName;
1101    CPDF_NameTree nameTree(pDoc, "Dests");
1102    int count = nameTree.GetCount();
1103    if (index >= count) {
1104      const CPDF_Dictionary* pDest = pRoot->GetDictFor("Dests");
1105      if (!pDest)
1106        return nullptr;
1107  
1108      pdfium::base::CheckedNumeric<int> checked_count = count;
1109      checked_count += pDest->size();
1110      if (!checked_count.IsValid() || index >= checked_count.ValueOrDie())
1111        return nullptr;
1112  
1113      index -= count;
1114      int i = 0;
1115      ByteStringView bsName;
1116      CPDF_DictionaryLocker locker(pDest);
1117      for (const auto& it : locker) {
1118        bsName = it.first.AsStringView();
1119        pDestObj = it.second.Get();
1120        if (!pDestObj)
1121          continue;
1122        if (i == index)
1123          break;
1124        i++;
1125      }
1126      wsName = PDF_DecodeText(bsName.raw_span());
1127    } else {
1128      pDestObj = nameTree.LookupValueAndName(index, &wsName);
1129    }
1130    if (!pDestObj)
1131      return nullptr;
1132    if (CPDF_Dictionary* pDict = pDestObj->AsDictionary()) {
1133      pDestObj = pDict->GetArrayFor("D");
1134      if (!pDestObj)
1135        return nullptr;
1136    }
1137    if (!pDestObj->IsArray())
1138      return nullptr;
1139  
1140    ByteString utf16Name = wsName.ToUTF16LE();
1141    int len = utf16Name.GetLength();
1142    if (!buffer) {
1143      *buflen = len;
1144    } else if (len <= *buflen) {
1145      memcpy(buffer, utf16Name.c_str(), len);
1146      *buflen = len;
1147    } else {
1148      *buflen = -1;
1149    }
1150    return FPDFDestFromCPDFArray(pDestObj->AsArray());
1151  }
1152