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