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 "fpdfsdk/cpdfsdk_customaccess.h"
44 #include "fpdfsdk/cpdfsdk_formfillenvironment.h"
45 #include "fpdfsdk/cpdfsdk_helpers.h"
46 #include "fpdfsdk/cpdfsdk_pageview.h"
47 #include "fpdfsdk/cpdfsdk_renderpage.h"
48 #include "fxjs/ijs_runtime.h"
49 #include "public/fpdf_formfill.h"
50 #include "third_party/base/check_op.h"
51 #include "third_party/base/numerics/safe_conversions.h"
52 #include "third_party/base/ptr_util.h"
53 #include "third_party/base/span.h"
54
55 #if defined(_SKIA_SUPPORT_)
56 #include "third_party/skia/include/core/SkPictureRecorder.h" // nogncheck
57 #include "third_party/skia/include/core/SkRect.h" // nogncheck
58 #endif // defined(_SKIA_SUPPORT_)
59
60 #ifdef PDF_ENABLE_V8
61 #include "fxjs/cfx_v8_array_buffer_allocator.h"
62 #include "third_party/base/no_destructor.h"
63 #endif
64
65 #ifdef PDF_ENABLE_XFA
66 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
67 #include "fpdfsdk/fpdfxfa/cpdfxfa_page.h"
68 #endif // PDF_ENABLE_XFA
69
70 #if BUILDFLAG(IS_WIN)
71 #include "core/fpdfapi/render/cpdf_progressiverenderer.h"
72 #include "core/fpdfapi/render/cpdf_windowsrenderdevice.h"
73 #include "public/fpdf_edit.h"
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 pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
550 CPDF_PageRenderContext* pContext = pOwnedContext.get();
551 CPDF_Page::RenderContextClearer clearer(pPage);
552 pPage->SetRenderContext(std::move(pOwnedContext));
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 pContext->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
568 dc, render_data->GetPSFontTracker());
569 CPDFSDK_RenderPageWithContext(pContext, 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 |pDevice|.
579 pBitmap->Create(size_x, size_y, FXDIB_Format::kArgb);
580 pBitmap->Clear(0x00ffffff);
581 CFX_DefaultRenderDevice* pDevice = new CFX_DefaultRenderDevice;
582 pContext->m_pDevice = pdfium::WrapUnique(pDevice);
583 pDevice->Attach(pBitmap);
584 if (bHasMask) {
585 pContext->m_pOptions = std::make_unique<CPDF_RenderOptions>();
586 pContext->m_pOptions->GetOptions().bBreakForMasks = true;
587 }
588
589 CPDFSDK_RenderPageWithContext(pContext, 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(pDst->GetBuffer().first(pBitmap->GetPitch() * size_y),
601 -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 pContext->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 pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
628 pContext = pOwnedContext.get();
629 pPage->SetRenderContext(std::move(pOwnedContext));
630 pContext->m_pDevice = std::make_unique<CPDF_WindowsRenderDevice>(
631 dc, render_data->GetPSFontTracker());
632 pContext->m_pOptions = std::make_unique<CPDF_RenderOptions>();
633 pContext->m_pOptions->GetOptions().bBreakForMasks = true;
634
635 CPDFSDK_RenderPageWithContext(pContext, 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(pContext->m_pDevice.get(), bitmaps[i], bitmap_areas[i]);
645 }
646 // Render the next portion of page.
647 pContext->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 pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
668 CPDF_PageRenderContext* pContext = pOwnedContext.get();
669 CPDF_Page::RenderContextClearer clearer(pPage);
670 pPage->SetRenderContext(std::move(pOwnedContext));
671
672 auto pOwnedDevice = std::make_unique<CFX_DefaultRenderDevice>();
673 CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
674 pContext->m_pDevice = std::move(pOwnedDevice);
675
676 RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
677 pDevice->AttachWithRgbByteOrder(pBitmap, !!(flags & FPDF_REVERSE_BYTE_ORDER));
678 CPDFSDK_RenderPageWithContext(pContext, pPage, start_x, start_y, size_x,
679 size_y, rotate, flags, /*color_scheme=*/nullptr,
680 /*need_to_restore=*/true,
681 /*pause=*/nullptr);
682
683 #if defined(_SKIA_SUPPORT_)
684 if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
685 pBitmap->UnPreMultiply();
686 }
687 #endif
688 }
689
690 FPDF_EXPORT void FPDF_CALLCONV
FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap,FPDF_PAGE page,const FS_MATRIX * matrix,const FS_RECTF * clipping,int flags)691 FPDF_RenderPageBitmapWithMatrix(FPDF_BITMAP bitmap,
692 FPDF_PAGE page,
693 const FS_MATRIX* matrix,
694 const FS_RECTF* clipping,
695 int flags) {
696 if (!bitmap)
697 return;
698
699 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
700 if (!pPage)
701 return;
702
703 auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
704 CPDF_PageRenderContext* pContext = pOwnedContext.get();
705 CPDF_Page::RenderContextClearer clearer(pPage);
706 pPage->SetRenderContext(std::move(pOwnedContext));
707
708 auto pOwnedDevice = std::make_unique<CFX_DefaultRenderDevice>();
709 CFX_DefaultRenderDevice* pDevice = pOwnedDevice.get();
710 pContext->m_pDevice = std::move(pOwnedDevice);
711
712 RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
713 pDevice->AttachWithRgbByteOrder(std::move(pBitmap),
714 !!(flags & FPDF_REVERSE_BYTE_ORDER));
715
716 CFX_FloatRect clipping_rect;
717 if (clipping)
718 clipping_rect = CFXFloatRectFromFSRectF(*clipping);
719 FX_RECT clip_rect = clipping_rect.ToFxRect();
720
721 const FX_RECT rect(0, 0, pPage->GetPageWidth(), pPage->GetPageHeight());
722 CFX_Matrix transform_matrix = pPage->GetDisplayMatrix(rect, 0);
723 if (matrix)
724 transform_matrix *= CFXMatrixFromFSMatrix(*matrix);
725 CPDFSDK_RenderPage(pContext, pPage, transform_matrix, clip_rect, flags,
726 /*color_scheme=*/nullptr);
727 }
728
729 #if defined(_SKIA_SUPPORT_)
FPDF_RenderPageSkp(FPDF_PAGE page,int size_x,int size_y)730 FPDF_EXPORT FPDF_RECORDER FPDF_CALLCONV FPDF_RenderPageSkp(FPDF_PAGE page,
731 int size_x,
732 int size_y) {
733 auto skDevice = std::make_unique<CFX_DefaultRenderDevice>();
734 std::unique_ptr<SkPictureRecorder> recorder =
735 skDevice->CreateRecorder(SkRect::MakeWH(size_x, size_y));
736
737 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
738 if (!pPage) {
739 // The equivalent bitmap APIs don't signal failure in this case, but defer
740 // the real work to a later call to `FPDF_FFLDraw()`. This is the case for
741 // XFA pages, for example.
742 //
743 // The caller still needs the `SkPictureRecorder` in order to call
744 // `FPDF_FFLRecord()` later.
745 return recorder.release();
746 }
747
748 auto pOwnedContext = std::make_unique<CPDF_PageRenderContext>();
749 pOwnedContext->m_pDevice = std::move(skDevice);
750
751 CPDF_Page::RenderContextClearer clearer(pPage);
752 CPDF_PageRenderContext* pContext = pOwnedContext.get();
753 pPage->SetRenderContext(std::move(pOwnedContext));
754
755 CPDFSDK_RenderPageWithContext(pContext, pPage, 0, 0, size_x, size_y, 0, 0,
756 /*color_scheme=*/nullptr,
757 /*need_to_restore=*/true, /*pause=*/nullptr);
758
759 return recorder.release();
760 }
761 #endif // defined(_SKIA_SUPPORT_)
762
FPDF_ClosePage(FPDF_PAGE page)763 FPDF_EXPORT void FPDF_CALLCONV FPDF_ClosePage(FPDF_PAGE page) {
764 if (!page)
765 return;
766
767 // Take it back across the API and hold for duration of this function.
768 RetainPtr<IPDF_Page> pPage;
769 pPage.Unleak(IPDFPageFromFPDFPage(page));
770
771 if (pPage->AsXFAPage())
772 return;
773
774 // This will delete the PageView object corresponding to |pPage|. We must
775 // cleanup the PageView before releasing the reference on |pPage| as it will
776 // attempt to reset the PageView during destruction.
777 pPage->AsPDFPage()->ClearView();
778 }
779
FPDF_CloseDocument(FPDF_DOCUMENT document)780 FPDF_EXPORT void FPDF_CALLCONV FPDF_CloseDocument(FPDF_DOCUMENT document) {
781 // Take it back across the API and throw it away,
782 std::unique_ptr<CPDF_Document>(CPDFDocumentFromFPDFDocument(document));
783 }
784
FPDF_GetLastError()785 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetLastError() {
786 return FXSYS_GetLastError();
787 }
788
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)789 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_DeviceToPage(FPDF_PAGE page,
790 int start_x,
791 int start_y,
792 int size_x,
793 int size_y,
794 int rotate,
795 int device_x,
796 int device_y,
797 double* page_x,
798 double* page_y) {
799 if (!page || !page_x || !page_y)
800 return false;
801
802 IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
803 const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
804 absl::optional<CFX_PointF> pos =
805 pPage->DeviceToPage(rect, rotate, CFX_PointF(device_x, device_y));
806 if (!pos.has_value())
807 return false;
808
809 *page_x = pos->x;
810 *page_y = pos->y;
811 return true;
812 }
813
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)814 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDF_PageToDevice(FPDF_PAGE page,
815 int start_x,
816 int start_y,
817 int size_x,
818 int size_y,
819 int rotate,
820 double page_x,
821 double page_y,
822 int* device_x,
823 int* device_y) {
824 if (!page || !device_x || !device_y)
825 return false;
826
827 IPDF_Page* pPage = IPDFPageFromFPDFPage(page);
828 const FX_RECT rect(start_x, start_y, start_x + size_x, start_y + size_y);
829 CFX_PointF page_point(static_cast<float>(page_x), static_cast<float>(page_y));
830 absl::optional<CFX_PointF> pos =
831 pPage->PageToDevice(rect, rotate, page_point);
832 if (!pos.has_value())
833 return false;
834
835 *device_x = FXSYS_roundf(pos->x);
836 *device_y = FXSYS_roundf(pos->y);
837 return true;
838 }
839
FPDFBitmap_Create(int width,int height,int alpha)840 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_Create(int width,
841 int height,
842 int alpha) {
843 auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
844 if (!pBitmap->Create(width, height,
845 alpha ? FXDIB_Format::kArgb : FXDIB_Format::kRgb32)) {
846 return nullptr;
847 }
848 return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
849 }
850
FPDFBitmap_CreateEx(int width,int height,int format,void * first_scan,int stride)851 FPDF_EXPORT FPDF_BITMAP FPDF_CALLCONV FPDFBitmap_CreateEx(int width,
852 int height,
853 int format,
854 void* first_scan,
855 int stride) {
856 FXDIB_Format fx_format;
857 switch (format) {
858 case FPDFBitmap_Gray:
859 fx_format = FXDIB_Format::k8bppRgb;
860 break;
861 case FPDFBitmap_BGR:
862 fx_format = FXDIB_Format::kRgb;
863 break;
864 case FPDFBitmap_BGRx:
865 fx_format = FXDIB_Format::kRgb32;
866 break;
867 case FPDFBitmap_BGRA:
868 fx_format = FXDIB_Format::kArgb;
869 break;
870 default:
871 return nullptr;
872 }
873
874 // Ensure external memory is good at least for the duration of this call.
875 UnownedPtr<uint8_t> pChecker(static_cast<uint8_t*>(first_scan));
876 auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
877 if (!pBitmap->Create(width, height, fx_format, pChecker, stride))
878 return nullptr;
879
880 return FPDFBitmapFromCFXDIBitmap(pBitmap.Leak());
881 }
882
FPDFBitmap_GetFormat(FPDF_BITMAP bitmap)883 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetFormat(FPDF_BITMAP bitmap) {
884 if (!bitmap)
885 return FPDFBitmap_Unknown;
886
887 FXDIB_Format format = CFXDIBitmapFromFPDFBitmap(bitmap)->GetFormat();
888 switch (format) {
889 case FXDIB_Format::k8bppRgb:
890 case FXDIB_Format::k8bppMask:
891 return FPDFBitmap_Gray;
892 case FXDIB_Format::kRgb:
893 return FPDFBitmap_BGR;
894 case FXDIB_Format::kRgb32:
895 return FPDFBitmap_BGRx;
896 case FXDIB_Format::kArgb:
897 return FPDFBitmap_BGRA;
898 default:
899 return FPDFBitmap_Unknown;
900 }
901 }
902
FPDFBitmap_FillRect(FPDF_BITMAP bitmap,int left,int top,int width,int height,FPDF_DWORD color)903 FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_FillRect(FPDF_BITMAP bitmap,
904 int left,
905 int top,
906 int width,
907 int height,
908 FPDF_DWORD color) {
909 if (!bitmap)
910 return;
911
912 CFX_DefaultRenderDevice device;
913 RetainPtr<CFX_DIBitmap> pBitmap(CFXDIBitmapFromFPDFBitmap(bitmap));
914 device.Attach(pBitmap);
915 if (!pBitmap->IsAlphaFormat())
916 color |= 0xFF000000;
917 device.FillRect(FX_RECT(left, top, left + width, top + height),
918 static_cast<uint32_t>(color));
919 }
920
FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap)921 FPDF_EXPORT void* FPDF_CALLCONV FPDFBitmap_GetBuffer(FPDF_BITMAP bitmap) {
922 return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetBuffer().data()
923 : nullptr;
924 }
925
FPDFBitmap_GetWidth(FPDF_BITMAP bitmap)926 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetWidth(FPDF_BITMAP bitmap) {
927 return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetWidth() : 0;
928 }
929
FPDFBitmap_GetHeight(FPDF_BITMAP bitmap)930 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetHeight(FPDF_BITMAP bitmap) {
931 return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetHeight() : 0;
932 }
933
FPDFBitmap_GetStride(FPDF_BITMAP bitmap)934 FPDF_EXPORT int FPDF_CALLCONV FPDFBitmap_GetStride(FPDF_BITMAP bitmap) {
935 return bitmap ? CFXDIBitmapFromFPDFBitmap(bitmap)->GetPitch() : 0;
936 }
937
FPDFBitmap_Destroy(FPDF_BITMAP bitmap)938 FPDF_EXPORT void FPDF_CALLCONV FPDFBitmap_Destroy(FPDF_BITMAP bitmap) {
939 RetainPtr<CFX_DIBitmap> destroyer;
940 destroyer.Unleak(CFXDIBitmapFromFPDFBitmap(bitmap));
941 }
942
943 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document,int page_index,FS_SIZEF * size)944 FPDF_GetPageSizeByIndexF(FPDF_DOCUMENT document,
945 int page_index,
946 FS_SIZEF* size) {
947 if (!size)
948 return false;
949
950 auto* pDoc = CPDFDocumentFromFPDFDocument(document);
951 if (!pDoc)
952 return false;
953
954 #ifdef PDF_ENABLE_XFA
955 if (page_index < 0 || page_index >= FPDF_GetPageCount(document))
956 return false;
957
958 auto* pContext = static_cast<CPDFXFA_Context*>(pDoc->GetExtension());
959 if (pContext) {
960 RetainPtr<CPDFXFA_Page> pPage = pContext->GetOrCreateXFAPage(page_index);
961 if (!pPage)
962 return false;
963
964 size->width = pPage->GetPageWidth();
965 size->height = pPage->GetPageHeight();
966 return true;
967 }
968 #endif // PDF_ENABLE_XFA
969
970 RetainPtr<CPDF_Dictionary> pDict = pDoc->GetMutablePageDictionary(page_index);
971 if (!pDict)
972 return false;
973
974 auto page = pdfium::MakeRetain<CPDF_Page>(pDoc, std::move(pDict));
975 page->AddPageImageCache();
976 size->width = page->GetPageWidth();
977 size->height = page->GetPageHeight();
978 return true;
979 }
980
FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,int page_index,double * width,double * height)981 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetPageSizeByIndex(FPDF_DOCUMENT document,
982 int page_index,
983 double* width,
984 double* height) {
985 if (!width || !height)
986 return false;
987
988 FS_SIZEF size;
989 if (!FPDF_GetPageSizeByIndexF(document, page_index, &size))
990 return false;
991
992 *width = size.width;
993 *height = size.height;
994 return true;
995 }
996
997 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document)998 FPDF_VIEWERREF_GetPrintScaling(FPDF_DOCUMENT document) {
999 const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1000 if (!pDoc)
1001 return true;
1002 CPDF_ViewerPreferences viewRef(pDoc);
1003 return viewRef.PrintScaling();
1004 }
1005
1006 FPDF_EXPORT int FPDF_CALLCONV
FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document)1007 FPDF_VIEWERREF_GetNumCopies(FPDF_DOCUMENT document) {
1008 const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1009 if (!pDoc)
1010 return 1;
1011 CPDF_ViewerPreferences viewRef(pDoc);
1012 return viewRef.NumCopies();
1013 }
1014
1015 FPDF_EXPORT FPDF_PAGERANGE FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document)1016 FPDF_VIEWERREF_GetPrintPageRange(FPDF_DOCUMENT document) {
1017 const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1018 if (!pDoc)
1019 return nullptr;
1020 CPDF_ViewerPreferences viewRef(pDoc);
1021
1022 // Unretained reference in public API. NOLINTNEXTLINE
1023 return FPDFPageRangeFromCPDFArray(viewRef.PrintPageRange());
1024 }
1025
1026 FPDF_EXPORT size_t FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange)1027 FPDF_VIEWERREF_GetPrintPageRangeCount(FPDF_PAGERANGE pagerange) {
1028 const CPDF_Array* pArray = CPDFArrayFromFPDFPageRange(pagerange);
1029 return pArray ? pArray->size() : 0;
1030 }
1031
1032 FPDF_EXPORT int FPDF_CALLCONV
FPDF_VIEWERREF_GetPrintPageRangeElement(FPDF_PAGERANGE pagerange,size_t index)1033 FPDF_VIEWERREF_GetPrintPageRangeElement(FPDF_PAGERANGE pagerange,
1034 size_t index) {
1035 const CPDF_Array* pArray = CPDFArrayFromFPDFPageRange(pagerange);
1036 if (!pArray || index >= pArray->size())
1037 return -1;
1038 return pArray->GetIntegerAt(index);
1039 }
1040
1041 FPDF_EXPORT FPDF_DUPLEXTYPE FPDF_CALLCONV
FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document)1042 FPDF_VIEWERREF_GetDuplex(FPDF_DOCUMENT document) {
1043 const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1044 if (!pDoc)
1045 return DuplexUndefined;
1046 CPDF_ViewerPreferences viewRef(pDoc);
1047 ByteString duplex = viewRef.Duplex();
1048 if ("Simplex" == duplex)
1049 return Simplex;
1050 if ("DuplexFlipShortEdge" == duplex)
1051 return DuplexFlipShortEdge;
1052 if ("DuplexFlipLongEdge" == duplex)
1053 return DuplexFlipLongEdge;
1054 return DuplexUndefined;
1055 }
1056
1057 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,FPDF_BYTESTRING key,char * buffer,unsigned long length)1058 FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document,
1059 FPDF_BYTESTRING key,
1060 char* buffer,
1061 unsigned long length) {
1062 const CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1063 if (!pDoc)
1064 return 0;
1065
1066 CPDF_ViewerPreferences viewRef(pDoc);
1067 absl::optional<ByteString> bsVal = viewRef.GenericName(key);
1068 if (!bsVal.has_value())
1069 return 0;
1070
1071 return NulTerminateMaybeCopyAndReturnLength(bsVal.value(), buffer, length);
1072 }
1073
1074 FPDF_EXPORT FPDF_DWORD FPDF_CALLCONV
FPDF_CountNamedDests(FPDF_DOCUMENT document)1075 FPDF_CountNamedDests(FPDF_DOCUMENT document) {
1076 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1077 if (!pDoc)
1078 return 0;
1079
1080 const CPDF_Dictionary* pRoot = pDoc->GetRoot();
1081 if (!pRoot)
1082 return 0;
1083
1084 auto name_tree = CPDF_NameTree::Create(pDoc, "Dests");
1085 FX_SAFE_UINT32 count = name_tree ? name_tree->GetCount() : 0;
1086 RetainPtr<const CPDF_Dictionary> pOldStyleDests = pRoot->GetDictFor("Dests");
1087 if (pOldStyleDests)
1088 count += pOldStyleDests->size();
1089 return count.ValueOrDefault(0);
1090 }
1091
1092 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
FPDF_GetNamedDestByName(FPDF_DOCUMENT document,FPDF_BYTESTRING name)1093 FPDF_GetNamedDestByName(FPDF_DOCUMENT document, FPDF_BYTESTRING name) {
1094 if (!name || name[0] == 0)
1095 return nullptr;
1096
1097 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1098 if (!pDoc)
1099 return nullptr;
1100
1101 ByteString dest_name(name);
1102
1103 // TODO(tsepez): murky ownership, should caller get a reference?
1104 // Unretained reference in public API. NOLINTNEXTLINE
1105 return FPDFDestFromCPDFArray(CPDF_NameTree::LookupNamedDest(pDoc, dest_name));
1106 }
1107
1108 #ifdef PDF_ENABLE_V8
FPDF_GetRecommendedV8Flags()1109 FPDF_EXPORT const char* FPDF_CALLCONV FPDF_GetRecommendedV8Flags() {
1110 // Use interpreted JS only to avoid RWX pages in our address space. Also,
1111 // --jitless implies --no-expose-wasm, which reduce exposure since no PDF
1112 // should contain web assembly.
1113 return "--jitless";
1114 }
1115
FPDF_GetArrayBufferAllocatorSharedInstance()1116 FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetArrayBufferAllocatorSharedInstance() {
1117 static pdfium::base::NoDestructor<CFX_V8ArrayBufferAllocator> allocator;
1118 return allocator.get();
1119 }
1120 #endif // PDF_ENABLE_V8
1121
1122 #ifdef PDF_ENABLE_XFA
FPDF_BStr_Init(FPDF_BSTR * bstr)1123 FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Init(FPDF_BSTR* bstr) {
1124 if (!bstr)
1125 return -1;
1126
1127 bstr->str = nullptr;
1128 bstr->len = 0;
1129 return 0;
1130 }
1131
FPDF_BStr_Set(FPDF_BSTR * bstr,const char * cstr,int length)1132 FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Set(FPDF_BSTR* bstr,
1133 const char* cstr,
1134 int length) {
1135 if (!bstr || !cstr)
1136 return -1;
1137
1138 if (length == -1)
1139 length = pdfium::base::checked_cast<int>(strlen(cstr));
1140
1141 if (length == 0) {
1142 FPDF_BStr_Clear(bstr);
1143 return 0;
1144 }
1145
1146 if (bstr->str && bstr->len < length)
1147 bstr->str = FX_Realloc(char, bstr->str, length + 1);
1148 else if (!bstr->str)
1149 bstr->str = FX_Alloc(char, length + 1);
1150
1151 bstr->str[length] = 0;
1152 memcpy(bstr->str, cstr, length);
1153 bstr->len = length;
1154 return 0;
1155 }
1156
FPDF_BStr_Clear(FPDF_BSTR * bstr)1157 FPDF_EXPORT FPDF_RESULT FPDF_CALLCONV FPDF_BStr_Clear(FPDF_BSTR* bstr) {
1158 if (!bstr)
1159 return -1;
1160
1161 if (bstr->str) {
1162 FX_Free(bstr->str);
1163 bstr->str = nullptr;
1164 }
1165 bstr->len = 0;
1166 return 0;
1167 }
1168 #endif // PDF_ENABLE_XFA
1169
FPDF_GetNamedDest(FPDF_DOCUMENT document,int index,void * buffer,long * buflen)1170 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDF_GetNamedDest(FPDF_DOCUMENT document,
1171 int index,
1172 void* buffer,
1173 long* buflen) {
1174 if (!buffer)
1175 *buflen = 0;
1176
1177 if (index < 0)
1178 return nullptr;
1179
1180 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
1181 if (!pDoc)
1182 return nullptr;
1183
1184 const CPDF_Dictionary* pRoot = pDoc->GetRoot();
1185 if (!pRoot)
1186 return nullptr;
1187
1188 auto name_tree = CPDF_NameTree::Create(pDoc, "Dests");
1189 size_t name_tree_count = name_tree ? name_tree->GetCount() : 0;
1190 RetainPtr<const CPDF_Object> pDestObj;
1191 WideString wsName;
1192 if (static_cast<size_t>(index) >= name_tree_count) {
1193 // If |index| is out of bounds, then try to retrieve the Nth old style named
1194 // destination. Where N is 0-indexed, with N = index - name_tree_count.
1195 RetainPtr<const CPDF_Dictionary> pDest = pRoot->GetDictFor("Dests");
1196 if (!pDest)
1197 return nullptr;
1198
1199 FX_SAFE_INT32 checked_count = name_tree_count;
1200 checked_count += pDest->size();
1201 if (!checked_count.IsValid() || index >= checked_count.ValueOrDie())
1202 return nullptr;
1203
1204 index -= name_tree_count;
1205 int i = 0;
1206 ByteStringView bsName;
1207 CPDF_DictionaryLocker locker(pDest);
1208 for (const auto& it : locker) {
1209 bsName = it.first.AsStringView();
1210 pDestObj = it.second;
1211 if (i == index)
1212 break;
1213 i++;
1214 }
1215 wsName = PDF_DecodeText(bsName.raw_span());
1216 } else {
1217 pDestObj = name_tree->LookupValueAndName(index, &wsName);
1218 }
1219 if (!pDestObj)
1220 return nullptr;
1221 if (const CPDF_Dictionary* pDict = pDestObj->AsDictionary()) {
1222 pDestObj = pDict->GetArrayFor("D");
1223 if (!pDestObj)
1224 return nullptr;
1225 }
1226 if (!pDestObj->IsArray())
1227 return nullptr;
1228
1229 ByteString utf16Name = wsName.ToUTF16LE();
1230 int len = pdfium::base::checked_cast<int>(utf16Name.GetLength());
1231 if (!buffer) {
1232 *buflen = len;
1233 } else if (len <= *buflen) {
1234 memcpy(buffer, utf16Name.c_str(), len);
1235 *buflen = len;
1236 } else {
1237 *buflen = -1;
1238 }
1239 return FPDFDestFromCPDFArray(pDestObj->AsArray());
1240 }
1241
FPDF_GetXFAPacketCount(FPDF_DOCUMENT document)1242 FPDF_EXPORT int FPDF_CALLCONV FPDF_GetXFAPacketCount(FPDF_DOCUMENT document) {
1243 CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
1244 if (!doc)
1245 return -1;
1246
1247 return fxcrt::CollectionSize<int>(
1248 GetXFAPackets(GetXFAEntryFromDocument(doc)));
1249 }
1250
1251 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetXFAPacketName(FPDF_DOCUMENT document,int index,void * buffer,unsigned long buflen)1252 FPDF_GetXFAPacketName(FPDF_DOCUMENT document,
1253 int index,
1254 void* buffer,
1255 unsigned long buflen) {
1256 CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
1257 if (!doc || index < 0)
1258 return 0;
1259
1260 std::vector<XFAPacket> xfa_packets =
1261 GetXFAPackets(GetXFAEntryFromDocument(doc));
1262 if (static_cast<size_t>(index) >= xfa_packets.size())
1263 return 0;
1264
1265 return NulTerminateMaybeCopyAndReturnLength(xfa_packets[index].name, buffer,
1266 buflen);
1267 }
1268
1269 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_GetXFAPacketContent(FPDF_DOCUMENT document,int index,void * buffer,unsigned long buflen,unsigned long * out_buflen)1270 FPDF_GetXFAPacketContent(FPDF_DOCUMENT document,
1271 int index,
1272 void* buffer,
1273 unsigned long buflen,
1274 unsigned long* out_buflen) {
1275 CPDF_Document* doc = CPDFDocumentFromFPDFDocument(document);
1276 if (!doc || index < 0 || !out_buflen)
1277 return false;
1278
1279 std::vector<XFAPacket> xfa_packets =
1280 GetXFAPackets(GetXFAEntryFromDocument(doc));
1281 if (static_cast<size_t>(index) >= xfa_packets.size())
1282 return false;
1283
1284 *out_buflen = DecodeStreamMaybeCopyAndReturnLength(
1285 xfa_packets[index].data,
1286 {static_cast<uint8_t*>(buffer), static_cast<size_t>(buflen)});
1287 return true;
1288 }
1289
1290 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetTrailerEnds(FPDF_DOCUMENT document,unsigned int * buffer,unsigned long length)1291 FPDF_GetTrailerEnds(FPDF_DOCUMENT document,
1292 unsigned int* buffer,
1293 unsigned long length) {
1294 auto* doc = CPDFDocumentFromFPDFDocument(document);
1295 if (!doc)
1296 return 0;
1297
1298 // Start recording trailer ends.
1299 auto* parser = doc->GetParser();
1300 std::vector<unsigned int> trailer_ends = parser->GetTrailerEnds();
1301 const unsigned long trailer_ends_len =
1302 fxcrt::CollectionSize<unsigned long>(trailer_ends);
1303 if (buffer && length >= trailer_ends_len) {
1304 for (size_t i = 0; i < trailer_ends_len; ++i)
1305 buffer[i] = trailer_ends[i];
1306 }
1307
1308 return trailer_ends_len;
1309 }
1310